web安全(4)-- sql注入

1.1 漏洞描述

    所謂SQL注入,就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。具體來說,它是利用現有應用程序,將(惡意)的SQL命令注入到後臺數據庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。比如先前的很多影視網站泄露VIP會員密碼大多就是通過WEB表單遞交查詢字符暴出的,這類表單特別容易受到SQL注入式攻擊

    根據相關技術原理,SQL注入可以分爲平臺層注入和代碼層注入。前者由不安全的數據庫配置或數據庫平臺的漏洞所致;後者主要是由於程序員對輸入未進行細緻地過濾,從而執行了非法的數據查詢。基於此,SQL注入的產生原因通常表現在以下幾方面:①不當的類型處理;②不安全的數據庫配置;③不合理的查詢集處理;④不當的錯誤處理;⑤轉義字符處理不合適;⑥多個提交處理不當。

1.2 漏洞危害

    數據庫信息泄漏:數據庫中存放的用戶的隱私信息的泄露。

    網頁篡改:通過操作數據庫對特定網頁進行篡改。

    網站被掛馬,傳播惡意軟件:修改數據庫一些字段的值,嵌入網馬鏈接,進行掛馬攻擊。

    數據庫被惡意操作:數據庫服務器被攻擊,數據庫的系統管理員帳戶被竄改。

    服務器被遠程控制,被安裝後門(WEBSHELL)。經由數據庫服務器提供的操作系統支持,讓黑客得以修改或控制操作系統。

    破壞硬盤數據,癱瘓全系統。

1.3 漏洞演示

    注入攻擊的本質就是把用戶輸入的數據當做代碼來執行,這裏有兩個關鍵條件:第一個是用戶能夠控制輸入,第二個是原本程序要執行的代碼,拼接了用戶輸入的數據。

    比如這個sql:"select id from users where username = '"+username +"' and password = '" + password +"'" 。如果用戶按着設計者的要求輸入正常的用戶名和密碼,這個沒有任何問題,可以正常登陸。但是這個sql因爲應用了sql拼接,用戶是可以控制輸入的,所以如果用戶拼接了可執行的數據,例如:username = "",password = "' or '1' = '1";那麼sql就變成了"select id from users where username = '' and password = '' or '1' = '1'",它等價於select id from users where '1' = '1'。這個SQL是恆真的,所以它就繞過了用戶名和密碼,登錄成功了!

    注入步驟:

    1)尋找注入點(URL地址欄、登陸界面、留言板、搜索框等)

    2)用戶自己構造SQL語句(如:’ or 1 = 1)

    3)將SQL語句發送給數據庫管理系統(DBMS)

    4)DBMS接收請求,並將該請求解釋成機器代碼指令,執行必要的存取操作

    5)DBMS接收返回的結果,並處理,返回給用戶

    因爲用戶構造了特殊的SQL語句,必定返回特殊的結果(只要你的SQL語句夠靈活的話)。

1.4 修復方案

    1)基本上大家都知道採用SQL語句預編譯和綁定變量,是防禦SQL注入的最佳方法。但是其中的深層次原因就不見得都理解了。

String sql = "select id, no from user where id=?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
ps.executeQuery();

    如上所示,就是典型的採用 SQL語句預編譯和綁定變量 。爲什麼這樣就可以防止SQL注入呢?

    其原因就是:採用了PreparedStatement,就會將SQL語句:"select id, no from user where id=?" 預先編譯好,也就是SQL引擎會預先進行語法分析,產生語法樹,生成執行計劃,也就是說,後面你輸入的參數,無論你輸入的是什麼,都不會影響該SQL語句的語法結構了,因爲語法分析已經完成了,而語法分析主要是分析SQL命令,比如 select ,from ,where ,and, or ,order by 等等。所以即使你後面輸入了這些SQL命令,也不會被當成SQL命令來執行了,因爲這些SQL命令的執行, 必須先的通過語法分析,生成執行計劃,既然語法分析已經完成,已經預編譯過了,那麼後面輸入的參數,是絕對不可能作爲SQL命令來執行的,只會被當做字符串字面值參數。所以SQL語句預編譯可以防禦SQL注入。

    2)但是不是所有場景都能夠採用SQL語句預編譯,有一些場景必須的採用字符串拼接的方式,此時,我們嚴格檢查參數的數據類型,還有可以使用一些安全函數來防止SQL注入。

    比如 String sql = "select id,no from user where id=" + id;

    在接收到用戶輸入的參數時,我們就嚴格檢查id,只能是int型。複雜情況可以使用正則表達式來判斷。這樣也是可以防止SQL注入的。

    安全函數的使用,比如:

MySQLCodec codec = new MySQLCodec(Mode.STANDARD);
name = ESAPI.encoder().encodeForSQL(codec, name);
String sql = "select id,no from user where name=" + name;

    ESAPI.encoder().encodeForSQL(codec, name)

    該函數會將name中包含的一些特殊字符進行編碼,這樣SQL引擎就不會將name中的字符串當成SQL命令來進行語法分析了。

    注:實際項目中,一般我們都是採用各種的框架,比如ibatis,hibernate,mybatis等等。他們一般也默認就是SQL預編譯的。對於ibatis/mybatis,如果使用的是#{name}形式的,那麼就是SQL預編譯,使用${name}就不是SQL預編譯的。

    3)使用正則表達式過濾:

private String CHECKSQL = “^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$”;
//判斷是否匹配:
Pattern.matches(CHECKSQL,targerStr);

//下面是具體的正則表達式:
//檢測SQL meta-characters的正則表達式 :
/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix

//修正檢測SQL meta-characters的正則表達式 :
/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(:))/i

//典型的SQL注入攻擊的正則表達式 :
/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix

//檢測SQL注入,UNION查詢關鍵字的正則表達式 :
/((\%27)|(\’))union/ix(\%27)|(\’)

//檢測MSSQL Server SQL注入攻擊的正則表達式:
/exec(\s|\+)+(s|x)p\w+/ix

//其實可以簡單的使用replace方法也可以實現上訴功能:
public static String TransactSQLInjection(String str){
    return str.replaceAll(".*([';]+|(--)+).*", " ");
}

    總的來說有以下幾點:

    a)永遠不要信任用戶的輸入,要對用戶的輸入進行校驗,可以通過正則表達式,或限制長度,對單引號和雙"-"進行轉換等。

    b)永遠不要使用動態拼裝SQL,可以使用參數化的SQL或者直接使用存儲過程進行數據查詢存取。

    c)永遠不要使用管理員權限的數據庫連接,爲每個應用使用單獨的權限有限的數據庫連接。

    d)不要把機密信息明文存放,請加密或者hash掉密碼和敏感的信息。

    e)應用的異常信息應該給出儘可能少的提示,最好使用自定義的錯誤信息對原始錯誤信息進行包裝,把異常信息存放在獨立的表中。

1.5 相關參考

http://www.cnblogs.com/smilewxt/p/4229810.html

http://blog.csdn.net/quiet_girl/article/details/50586809

發佈了38 篇原創文章 · 獲贊 30 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章