sql注入問題從WEB誕生到如今也一直沒停過,各類大小公司都出現過sql注入問題,致使被拖庫,而後存在社工庫撞庫等一系列影響。java
防止sql注入我的理解最主要的就一點,那就是變量所有參數化,能根本的解決sql注入問題。好比代碼裏寫sql或數據庫層面寫存儲過程,只要不拼接sql語句,不執行動態sql語句,那就能防止sql注入。python
但實際狀況sql語句可能很是複雜。不少年前有討論很熱的話題,很是複雜的業務邏輯到底用sql語句在存儲過程裏來實現,仍是在代碼service層來實現複雜的業務邏輯。好比電信移動不少老項目都是有很是複雜的存儲過程或函數來實現業務,這裏不說到底放哪裏好,由於和實際業務狀況有關係。無論把這麼複雜的業務邏輯在代碼層面service或dao裏實現仍是存儲過程實現,都須要防止sql注入問題。mysql
和數據庫交互的業務系統,去掉業務邏輯層面,無論應用服務器仍是數據庫服務器,就剩下sql的操做。這裏說下我認爲的幾種和實際狀況最接近的寫sql語句的狀況:sql
一、where裏是單條件或條件個數明確的sql語句:shell
好比按照用戶ID查詢用戶信息;1個訂單多個產品,按照訂單ID,產品ID查詢該訂單裏某個產品的信息,where條件是明確的。數據庫
二、where條件不明確的,不明確的緣由是須要根據用戶的選擇做爲條件:數組
好比查詢訂單,訂單有多個元素:OrderID,UserID,ProductID,ProductName,CreateTime,Status,如今是多條件搜索頁面,用戶可能選擇按照CreateTime和Status做爲條件查詢某個時間段訂單狀態爲支付完成的,where條件爲:where CreateTime=.. and Status=..,若是用戶按照其餘元素搜索,where寫法又不同。服務器
三、where條件是一個in範圍的或like查詢的:session
好比where UserID in (1,2,3,4)。oracle
這裏最簡單的是第1個狀況,條件明確,直接把每一個肯定的條件參數化查詢sql便可。
第2個狀況稍微複雜點,通常開發人員很容易拼接sql語句,尤爲條件很是多的狀況下,按照用戶查詢的不一樣條件,if else..按照條件直接拼接sql語句,這樣就形成了sql注入。實際處理時,能夠在第一個if前定義一個List變量,針對每一個if條件,若是符合if條件,sql語句就參數化一個佔位符,List變量add一個用戶傳入的條件。下面是示例代碼:
if (null != begindate && !"".equals(begindate)) {
// 開始日期
sqlBuilder.append(" and beginDate >= ? ");
params.add(DateUtil.conversionDate6(begindate.trim() + " 00:00"));
}
sqlBuilder用於參數化拼接sql,params是參數化集合List params,後面再參數化sql查詢:
Query query = sessionFactory.getCurrentSession().createQuery(sqlBuilder.toString());
private void setQueryParamter(Query query, List params) {
if (!params.isEmpty()) {
for (int i = 0; i < params.size(); i++) {
query.setParameter(i, params.get(i));
}
}
}
第3個狀況in會有一個問題,就是好比in(?)?傳入的問題,須要把?參數傳爲一個數組,常常出現的問題可能傳入一個字符串致使查詢不出來,好比:
String sqlStr = "select * from user i where i.id in (:ids)";
Query query = sessionFactory.getCurrentSession().createQuery(queryString);
query.setParameterList("ids", idsValue);
idsValue可能會被賦值爲"'1','2','3'"的字符串,實際應該爲String[] splits=...的一個數組。
like裏應該以下:
sql.append("and name like ? ");
list.add("%"+param+"%");
而不該該寫爲:
sql.append("and name like '%?%' ");
list.add(param);
list做爲參數化傳入的參數。
上面的示例代碼是java的示例代碼,其餘.NET和PHP也能夠參考。不一樣數據庫參數化的佔位符號不同,好比msql是@,oracle是:,mysql是?,mysql裏用名字取名的佔位符是":名字",好比前面的:ids。
上面說了如何防止sql注入,下面簡單介紹下神器sqlmap,它是一個典型的sql注入檢測和利用工具。
sqlmap支持5種sql注入模式:
一、基於布爾的盲注(Boolean-based blind SQL injection),便可以根據返回頁面判斷條件真假的注入。
好比對參數加一個' and '1'='1和' and '1'='2,若是第一個能查詢出來,第二個不行,則說明能夠注入,後面能夠根據字段and exist(...)不斷猜想。
二、基於時間的盲注(Time-based blind SQL injection),即不能根據頁面返回內容判斷任何信息,用條件語句查看時間延遲語句是否執行(即頁面返回時間是否增長)來判斷。
好比直接把某個參數的值刪除,改成if((ascii(mid(user(),1,1))=103 and sleep(3))若是ascii(mid(user(),1,1))=103條件爲真,則會等待3秒才返回結果,若是ascii(mid(user(),1,1))=103爲假,則直接返回頁面,這樣不斷猜想user()的第1,2,3個字符。
三、基於報錯注入(Error-based SQL injection),即頁面會返回錯誤信息,或者把注入的語句的結果直接返回在頁面中。
好比在條件後面增長+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,user(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a),經過報錯把user()顯示出來,這個方法是最快的。
這裏報錯注入的sql很很差理解,首先它實現的前提是若是sql報錯了,後臺配置的按照sql怎麼出錯怎麼拋出異常到頁面(即非友好頁面),而後好比這裏報錯主要依靠floor(rand(0)*2),rand(0)是mysql的一個函數,他會返回0到1之間的隨機浮點數,rand(0)*2爲1到2之間的隨機浮點數,floor是往下取整的函數,好比floor(0.2)=0,floor(1.2)=1。這裏group by x就很是重要了,好比咱們查詢select 1 from information_schema.tables group by rand(0) limit 0,30;將會查詢30個1出來,緣由是group by 一個隨機數,每次都將查詢不一樣的數據出來,limit限制30就顯示30條。因此剛纔試的sql語句,由於group by x,x的值是隨機的,好比x值爲~root@127.0.0.1~0,也可能爲~root@127.0.0.1~1,group by 這個數據若是遇到兩個相同的group key,則就報錯了,若是頁面吧錯誤拋出到頁面,則能夠經過正則匹配~和~符號之間的爲user()的值,好比報錯:Duplicate entry '~root@127.0.0.1~1' for key 'group_key'。
四、聯合查詢注入(UNION query SQL injection),可使用union的狀況下的注入。
這個很好理解,就是在select後union把數據查詢出來,好比先在查詢條件後輸入order by 3,order by 4..到order by 8的時候報錯了,說明select了7條記錄。而後條件後輸入 and 1=2 union select 1,user(),database()...查詢7個數據出來,因爲第一個select後有了1=2條件不成i,所以只會查詢咱們寫的select語句,這樣覆蓋以前的查詢記錄返回出咱們須要的數據,這個速度也很是快。
五、堆查詢注入(Stacked queries SQL injection),能夠同時執行多條語句的執行時的注入。
好比用;分隔後執行其餘sql語句,通常用於更新數據或執行系統相關命令,好比在參數後面增長; update sysuser set password='' where username='admin',或好比sqlserver執行exec master..xp_cmdshell,執行cmd命令建立遠程登陸用戶等。
sqlmap很是強大,能夠get,post,httpheader的值變化識別是否存在注入,它能夠自動識別MySQL, Oracle, PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird, Sybase和SAP MaxDB數據庫,還有一些經常使用的繞過防止sql注入的替換符號,也能夠本身擴展python腳本繞過WAF之類的防注入程序,經常使用參數和經常使用檢測命令後續單獨再寫一個blog文章。
如需轉載,請註明來自:http://lawson.cnblogs.com