SQL Injection(SQL注入)

一、SQL Injection的原理
SQL Injection的實現方法和破壞作用有很多,但萬變不離其宗,其原理可以概括爲一句話 :SQL Injection就是向服務器端提交事先準備好的數據,拼湊出***者想要的SQL語句,以改變數據庫操作執行計劃。

這句話主要包含這麼三層意思:
1.***者通過何種途徑注入?
存在SQL Injection漏洞的地方都是應用程序需要根據客戶端環境構造SQL語句的地方。由此可以推論,只要存在"客戶端數據替換預定義變量"的地方,就有可能被注入。客戶端提交數據可以有很多種方式:GET,POST,Client-Agent,Cookie,Server Enviroment...
2.***者爲什麼可以將它想要的語句"注入"?
因爲服務器端應用程序採用拼湊(請特別留意這個詞)SQL語句的方式,這使得***者有機會在提交的數據中包含SQL關鍵字或者運算符,來構造他們想要的語句。
3.SQL Injection最終結果是什麼?
改變數據庫操作執行計劃。
這個結果不一定是惡意的,只要你的SQL語句沒有按照你預期的計劃(plan)執行,那麼就 可以視爲被注入了,不管提交數據的人是不是惡意的。


設有這樣的sql語句:

  1. update `tableName` set `columnName1` = " $Client_Submit_Data " where `PK_ID` = '1234'  


假設這個操作是要更新一篇文章的標題,很多人是不是會這麼構造SQL語句?我們看看$Cl ient_Submit_Data包含引號的情況,令

  1. $Client_Submit_Data = '誰能告訴我"sql injecti on"是什麼?' 


那麼sql語句將被拼湊成這樣:

  1. update `tableName` set `columnName1` = "誰能告訴我"sql injection"是什麼?" where `PK_ID` = '1234'  


執行結果很明顯,將執行這樣的語句:

  1. update `tableName` set `columnName1` = "誰能告訴我"  


where子句被忽略掉了,很遺憾,你的數據庫中所有文章標題都會被update爲"誰能告訴我 "
 
如何應對這種問題。
 
我認爲,徹底解決SQL Injection的最好方法是:避免拼湊SQL語句。這就是我在上面要大家特別注意拼湊這個詞的原因。
SQL Injection之所以有機可乘,是因爲絕大多數Server Application採用拼湊SQL語句的方式來構建應用程序(閱讀這個帖子的諸位,你們回首想想自己的項目,有幾個不是通過拼湊SQL語句的方式來操 作數據庫?想想你們見過的被注入的案例,有幾個不是採用的拼湊SQL語句的應用),所謂拼湊SQL語句,簡單一點說就是:用連接字符串操作將SQL關鍵字和客戶端提交的數據連接起來併發送給DBMS執行。這樣做直接導致 DBMS根本不知道你計劃(plan to)做什麼,而只知道你要(is to)做什麼,不是嗎,服務器端腳本總是將要執行的SQL語句構造好,然後發給數據庫,DBMS根本不知道客戶端數據 替換了變量之後,這個語句的執行計劃是否有變化。服務器端腳本總是粗暴的告訴DBMS:你只管這麼做好了,別問我爲什麼。就像上面我提到的更新文章標題的 例子,DBMS不知道你只想更新第1234篇文章的標題,它以爲你就是要把所有的標題都變成這個,因爲你的語句就是沒有where子句嘛!
 
說到這裏,可能大家都明白了,所謂的最好方法是Stored Procedure。Yes! That is!
要想做出安全可靠的Server application,你最好把自己當作兩個人,一個DBA,一個Coder,很多人往往只知道:我在做一個BBS,我在做一個留言本,我在做一個新聞發佈系統,我們的流程都是這樣的,給用戶一個表單,讓用戶提交, 然後去寫數據庫,用的時候根據條件把數據記錄找出來,然後顯示。沒事,如果你 是一個業餘愛好者,只想自己寫點小東西玩玩,這足夠了!如果你想把WebDev作爲你的職業,或者說,你想成爲一個非常專業的業餘愛好者,你必須當自己是 一個DBA+Coder,至於要不要是一個Designer就看你的能力和精力咯!
 
好了,點到爲止,我就說這麼多,徹底的解決方法是要在DBMS上寫入你的數據操作計劃,讓服務器在開始執行之前知道你的意圖,不要粗暴的告訴它:我就是要你執行這個命令,不要問我爲什麼!
 
實現方法嘛,目前比較普遍的,也比較容易實現的就是存儲過程了,應用存儲過程不僅可以從根本上解決SQL Injection這個安全問題,還會使得你的應用程序速度成倍增長(這個增長的幅度甚至可能達到一個數量級,這跟很多因素有關,不好一概而論),還會使 得你開發的系統更想大型系統,擁有更好的架構體系(例如MVC模式)。
 
在 MySQL 4.1.x及其後續版本和ODBC中,提供了一種叫做prepared statements的東西,它 本質上也是一種存儲過程,一種系統預置(相對於用戶自定義)的存儲過程。
如 果你沒有條件用上存儲過程(比如數據庫不支持,MySQL,Access,SQLite等都不支持),那麼就只能將SQL Injection扼殺在搖籃裏了。解決方法,我也只簡單的說一句:不要相信任何來自客戶端的數據。這個客戶端的數據,可以通過很多途徑被提交,比如 get,post ,cookie,browser參數,IP地址,等等,只要不是服務器上獲取的就都算客戶端數據,只要是客戶端數據,都是不可信的,在TCP/IP這個大 框架下,什麼都是可以僞造的,包括IP地址。
 
凡是來自 也無法窮舉可能被用於SQL Injection的符號和關鍵字,也無法預知替換掉他們是否會有副 作用,最好的辦法是不去判斷什麼數據不符合條件,而改由判斷什麼數據符合條件,假設你的一個系統用戶名只能是字母數字和下劃線,那麼你就可以用 [0-9a-zA-Z_]+這個正則來匹配它,如果不符合條件,拒之即可,這比費盡心思去過濾單引號分號逗號空格什麼的要明瞭和簡潔的多。 

 

附帶:PHP防注入的最簡單函數

 

  1. $_POST = sql_injection($_POST); 
  2. $_GET = sql_injection($_GET); 
  3.  
  4. function sql_injection($content)
  5. if (!get_magic_quotes_gpc()) { 
  6. if (is_array($content)) { 
  7. foreach ($content as $key=>$value) { 
  8. $content[$key] = addslashes($value); 
  9. else { 
  10. addslashes($content); 
  11. return $content

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章