Sql注入漏洞及綁定變量淺談

1 一個問題引發的思考
大家在羣裏討論了一個問題,奉文帥之命寫篇作文,且看:
String user_web = "user_web"
String sql = "update user  set user_web="+user_web+" where userid=2343";
大家看看這條sql有沒有問題,會將user_web字段 更新成什麼?
問題的結論是:執行後的記錄結果跟執行前一樣,(執行時的sql語句爲
update user  set user_web=user_web where userid=2343,
user_web字段值被update爲自己原有的值),這與作者的本意想違背卻很難被發現有問題。
原來的語句漏掉了一對單引號,正確的寫法應該是:
String sql = "update user  set user_web='"+user_web+"' where userid=2343";
用這種寫法將變量值傳遞到sql語句中,意圖是達到了,但不是好的方式,理由如下:
1. 可讀性差。單引號雙引號混雜(試想有多個變量的情況,再想下如果稍一不慎在前面的單引號雙引號直間多個空格又會怎樣?)
2. 會造成潛在的性能問題和sql注入漏洞(對測試代碼而言,這兩點可能要求不高,但養成良好的編碼習慣還是很重要的)
下面以非專業人員的角度大致分析下 '"+變量+"'(未採用綁定變量方式)這種方式組織sql爲什麼會造成潛在的性能問題和sql注入漏洞問題
2 性能問題
Sql代碼不採用綁定變量的方式可能會造成性能問題,表現在以下兩個方面:
1. 導致相同的測試計劃被重複執行
sql語句的執行過程分幾個步驟:語法檢查、分析、執行、返回結果。當一條sql通過語法檢查後,會在共享池裏尋找是否有跟其相同的語句,如果有則用已有的執行計劃執行sql語句,如果沒有找到,則生成執行計劃,然後才執行sql語句。可見,後者比前者多了額外的步驟,消耗了額外的CPU,並導致sql總體執行時間延長,而這裏的關鍵就是“共享池中是否有相同的sql語句”。
String username="test_xx";
String sql = "SELECT id,nick FROM user WHERE username='"+username+"'";
以這樣的方式傳遞到數據庫中的sql爲
SELECT id,nick FROM user WHERE username='test_xx'
假定這個語句是第一次執行,會生成執行計劃。當變量發生變化時(username="test_yy"),數據庫又接收到這樣的語句
SELECT id,nick FROM user WHERE username='test_yy'
Oracle不認爲以上兩條語句是相同的,因此又會生成執行計劃,而這兩者的執行計劃是一樣的(做了重複的工作)
2. 導致共享池中的sql語句過多,加速SQL老化,造成共享池內部結構頻繁維護。
如果一個某段程序未採用綁定變量的方式而又被大量調用,會導致共享池中不同的sql語句增多,而重用性極低,導致共享池內命中率下降。隨着sql數量過多,一些語句逐漸老化,最終被清理出共享池。 而維護共享池內部結構要消耗大量的CPU和內存資源。
3 Sql注入漏洞
不採用綁定變量的方式可能會造成sql注入漏洞,本文僅僅通過示例說明爲什麼會造成sql注入漏洞,不對攻擊方式、攻擊類型等展開。以一個用戶驗證爲例。
String sql = "SELECT id,nick FROM user WHERE username='"+username+"' AND password='"+password+"'";
以上代碼接收從客戶端傳來的username和password變量,在數據庫中查詢驗證。假設攻擊者從客戶端傳的username爲任意值(如test)password變量爲
1' or '1'='1
此時替換變量後的sql變爲
SELECT id,nick FROM user WHERE username='test' AND password='1' or '1'='1'
這樣得到的結果就是user表中的所有數據了。
4 使用綁定變量
以上兩種問題的解決方式就是使用綁定變量,就是在sql語句裏不直接寫變量,而是用佔位符,在執行時再把佔位符替換爲具體的變量值。代碼片段如下
String sql = "SELECT id,nick FROM user WHERE username=? AND password=?";
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
一些常用Jdbc工具對此進行了良好的封裝,使代碼更加簡潔。比如Spring的SimpleJdbcTemplate
String sql = "SELECT id,nick FROM user WHERE username=? AND password=?";
jdbcTemplate.queryForList(sql,username,password);
上面 ?的做佔位符的形式被稱爲順序佔位符,在傳參數值時必須注意順序對應,還有一種是名稱佔位符。同樣以SimpleJdbcTemplate爲例說明.
String sql = "SELECT id,nick FROM user WHERE username=:name AND password=:pass";
map.put("pass",password);
map.put("name",username);
jdbcTemplate.queryForList(sql,map);
上面的例子中的:name和:pass就是名稱佔位符,在執行時sql時再綁定變量。
iBatis中有兩種佔位符,#name# 和$name$兩種方式,需要注意的是前者會在執行sql時綁定變量,而後者直接是替換爲變量值,所以後者仍然存在sql注入漏洞問題。
5 未盡話題
接口測試要不要檢查sql注入漏洞問題?這個問題值得商榷,個人認爲通過常規的用例設計檢查sql注入漏洞恐怕不太可行(工作量太大效果不一定好),如果要做的話可藉助(或自己開發)一些工具,通過掃描靜態代碼再人工排查的方式進行。另外如果這項工作進行的太細緻,恐怕會跟安全測試的工作重疊太多,當然如果在測試過程中發現開發的代碼存在sql注入漏洞(這往往跟開發者編碼習慣有關)問題,一定不要放過,進行排查還是很有必要的
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章