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

作者: yedu  來源: RDC  發布時間: 2010-01-19 16:03  閱讀: 7802 次  推薦: 2   原文鏈接   [收藏]  

  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注入漏洞(這往往跟開發者編碼習慣有關)問題,一定不要放過,進行排查還是很有必要的。

2
0
 
標簽:Sql注入
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(1) 人氣()