php安全編程——sql注入攻擊
定義
-
SQL注入攻擊指的是通過構建特殊的輸入作為參數傳入Web應用程序,而這些輸入大都是SQL語法里的一些組合,通過執行SQL語句進而執行攻擊者所要的操作,其主要原因是程序沒有細致地過濾用戶輸入的數據,致使非法數據侵入系統。
-
根據相關技術原理,SQL注入可以分為平臺層注入和代碼層注入。前者由不安全的數據庫配置或數據庫平臺的漏洞所致;后者主要是由于程序員對輸入未進行細致地過濾,從而執行了非法的數據查詢。基于此,SQL注入的產生原因通常表現在以下幾方面:
-
不當的類型處理;
-
不安全的數據庫配置;
-
不合理的查詢集處理;
-
不當的錯誤處理;
-
轉義字符處理不合適;
- 多個提交處理不當。
-
不當的類型處理;
-
在某些表單中,用戶輸入的內容直接用來構造動態sql命令,或者作為存儲過程的輸入參數,這些表單特別容易受到sql注入的攻擊。而許多網站程序在編寫時,沒有對用戶輸入的合法性進行判斷或者程序中本身的變量處理不當,使應用程序存在安全隱患。這樣,用戶就可以提交一段數據庫查詢的代碼,根據程序返回的結果,獲得一些敏感的信息或者控制整個服務器,于是sql注入就發生了
常用技術
-
強制產生錯誤
對數據庫類型、版本等信息進行識別是此類型攻擊的動機所在。它的目的是收集數據庫的類型、結構等信息為其他類型的攻擊做準備,可謂是攻擊的一個預備步驟。利用應用程序服務器返回的默認錯誤信息而取得漏洞信息。
-
采用非主流通道技術
除HTTP響應外,能通過通道獲取數據,然而,通道大都依賴與數據庫支持的功能而存在,所以這項技術不完全適用于所有的數據庫平臺。SQL注入的非主流通道主要有E-mail、DNS以及數據庫連接,基本思想為:先對SQL查詢打包,然后借助非主流通道將信息反饋至攻擊者。
-
使用特殊的字符
不同的SQL數據庫有許多不同是特殊字符和變量,通過某些配置不安全或過濾不細致的應用系統能夠取得某些有用的信息,從而對進一步攻擊提供方向。
-
使用條件語句
此方式具體可分為基于內容、基于時間、基于錯誤三種形式。一般在經過常規訪問后加上條件語句,根據信息反饋來判定被攻擊的目標
-
利用存儲過程
通過某些標準存儲過程,數據庫廠商對數據庫的功能進行擴展的同時,系統也可與進行交互。部分存儲過程可以讓用戶自行定義。通過其他類型的攻擊收集到數據庫的類型、結構等信息后,便能夠建構執行存儲過程的命令。這種攻擊類型往往能達到遠程命令執行、特權擴張、拒絕服務的目的。
-
避開輸入過濾技術
雖然對于通常的編碼都可利用某些過濾技術進行SQL注入防范,但是鑒于此種情況下也有許多方法避開過濾,一般可達到此目的的技術手段包括SQL注釋和動態查詢的使用,利用截斷,URL編碼與空字節的使用,大小寫變種的使用以及嵌套剝離后的表達式等等。借助于此些手段,輸入構思后的查詢可以避開輸入過濾,從而攻擊者能獲得想要的查詢結果。
-
推斷技術
能夠明確數據庫模式、提取數據以及識別可注入參數。此種方式的攻擊通過網站對用戶輸入的反饋信息,對可注入參數、數據庫模式推斷,這種攻擊構造的查詢執行后獲得的答案只有真、假兩種。基于推斷的注入方式主要分為時間測定注入與盲注入兩種。前者是在注入語句里加入語句諸如“waitfor 100”,按照此查詢結果出現的時間對注入能否成功和數據值范圍的推導進行判定;后者主要是“and l=l”、“and l=2”兩種經典注入方法。這些方式均是對一些間接關聯且能取得回應的問題進行提問,進而通過響應信息推斷出想要信息,然后進行攻擊
防范方法
-
強制字符格式(類型)
-
對于整形變量, 運用 intval函數將數據轉換成整數
-
浮點型參數:運用 floatval或doubleval函數分別轉換單精度和雙精度浮點型參數
-
字符型參數:運用 addslashes函數來將單引號“’”轉換成“\’”,雙引號“"”轉換成“"”,反斜杠“\”轉換成“\”,NULL字符加上反斜杠“\”如果是字符型,先判斷magic_quotes_gpc是否為On,當不為On的時候運用 addslashes轉義特殊字符
-
對于整形變量, 運用 intval函數將數據轉換成整數
-
生產環境關閉數據庫錯誤提示,以避免攻擊者得到數據庫的相關信息。
-
SQL語句中包含變量加引號
SELECT * FROM article WHERE articleid='$id'
沒有把變量放進單引號中,那我們所提交的一切,只要包含空格,那空格后的變量都會作為SQL語句執行,給了攻擊者構造特殊sql語句的可能。因此,我們要養成給SQL語句中變量加引號的習慣
-
使用pdo 使用 prepared statements ( 預處理語句 )和參數化的查詢。這些SQL語句被發送到數據庫服務器,它的參數全都會被單獨解析。使用這種方式,攻擊者想注入惡意的SQL是不可能的
-
使用PDO訪問MySQL數據庫時,真正的real prepared statements 默認情況下是不使用的。為了解決這個問題,你必須禁用 prepared statements的仿真效果。下面是使用PDO創建鏈接的例子:
<?php $dbh = new PDO('mysql:dbname=mydb;host=127.0.0.1;charset=utf8', 'root', 'pass'); $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); ?>
這可以確保SQL語句和相應的值在傳遞到mysql服務器之前是不會被PHP解析的(禁止了所有可能的惡意SQL注入攻擊)
完整示例代碼:
<?php $dbh = new PDO("mysql:host=localhost; dbname=mydb", "root", "pass"); $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果 $dbh->exec("set names 'utf8'"); $sql="select * from table where username = ? and password = ?"; $query = $dbh->prepare($sql); $exeres = $query->execute(array($username, $pass)); if ($exeres) { while ($row = $query->fetch(PDO::FETCH_ASSOC)) { print_r($row); } } $dbh = null; ?>
-
-
如下幾種情況,pdo prepared statements 將不能起到防范的作用
-
PDO::ATTR_EMULATE_PREPARES 啟用或禁用預處理語句的模擬。 有些驅動不支持或有限度地支持本地預處理。使用此設置強制PDO總是模擬預處理語句(如果為 TRUE ),或試著使用本地預處理語句(如果為 FALSE)。如果驅動不能成功預處理當前查詢,它將總是回到模擬預處理語句上。
-
不能讓占位符 ? 代替一組值,這樣只會獲取到這組數據的第一個值,
select * from table where userid in ( ? );
如果要用in來查找,可以改用find_in_set()實現:
$ids = '1,2,3,4,5,6'; select * from table where find_in_set(userid, ?);
-
不能讓占位符代替數據表名或列名,如:
select * from table order by ?;
-
不能讓占位符 ? 代替任何其他SQL語法,如:
select extract( ? from addtime) as mytime from table;
-
PDO::ATTR_EMULATE_PREPARES 啟用或禁用預處理語句的模擬。 有些驅動不支持或有限度地支持本地預處理。使用此設置強制PDO總是模擬預處理語句(如果為 TRUE ),或試著使用本地預處理語句(如果為 FALSE)。如果驅動不能成功預處理當前查詢,它將總是回到模擬預處理語句上。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
