您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關如何從SQL寬字節注入認識的PDO原理和正確使用,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
隨著數據庫參數化查詢的方式越來越普遍,SQL注入漏洞較之于以前也大大減少,而PDO作為php中最典型的預編譯查詢方式,使用越來越廣泛。
眾所周知,PDO是php中防止SQL注入最好的方式,但并不是100%杜絕SQL注入的方式,關鍵還要看如何使用。
首先在本地新建一個庫和表,隨便寫點東西。
然后寫一個test.php,用PDO進行簡單的查詢:
<?php try { $db = new PDO('mysql:host=localhost;dbname=pdotest','root',''); } catch(Exception $e) { echo $e->getMessage(); } if(isset($_GET['id'])) { $id = $_GET['id']; } else { $id=1; } $query = "select balabala from table1 where 1=?"; echo "id:".$id."</br>"; $row = $db->prepare($query); $row->bindParam(1,$id); $row->execute(); $result = $row->fetch(PDO::FETCH_ASSOC); if($result) { echo "結果為:"; print_r($result); echo "</br>"; }
將輸入的內容和得到的結果打印在頁面上:
PDO與安全問題相關的主要的設置有下面三個:
PDO::ATTR_EMULATE_PREPARES
PDO::ATTR_ERRMODE
PDO::MYSQL_ATTR_MULTI_STATEMENTS
分別與模擬預編譯、報錯和多句執行有關。
PDO默認是允許多句執行和模擬預編譯的,在之前的很多文章中已經寫到,在參數可控的情況下,會導致堆疊注入。
例如我們把查詢語句改成:
$query = "select balabala from table1 where 1={$id}"; $row = $db->query($query);
則在$db->query()這一步執行之前,我們便可以對$query進行非法操作,那PDO相當于沒用:
如果我們在查詢語句中沒有可控的參數,并把輸入的參數按照prepare->bindParam->execute的方式去寫就一定沒有問題了嗎?
我們按如下語句進行查詢:
$query = "select balabala from table1 where 1=?"; $row = $db->prepare($query); $row->bindParam(1,$_GET[‘id’]); $row->execute();
我們在URL中隨便輸入一個參數:?id=asdasd,然后通過設置SET GLOBAL GENERAL_LOG=ON,從.log里實時監控,看看sql語句到底執行了什么:
我們發現模擬預編譯的請求發送方式和以往的mysqli并沒有什么區別,但我們注意到,在原有的查詢語句中對參數并沒有用單引號包裹,而在此卻用單引號進行了包裹,于是我們可以嘗試輸入一些特殊字符,比如單引號:
發現單引號被轉義了,這時我們不由得想到如果設置了gbk編碼會怎么樣:
我們會發現select * from table1成功執行了,盡管PDO只會返回一個結果,但是它的的確確執行了。
也就是說,即使查詢語句里沒有可控參數,只有?或者:id這類被綁定的參數,依然可以進行堆疊注入。
那如果把多句執行關掉呢?
我們把PDO::MYSQL_ATTR_MULTI_STATEMENTS設為false,重復上述操作:
發現已經行不通了。
實際也只執行了設置gbk這一條語句
但是這樣就結束了嗎?
為什么不試試union注入等其他方式呢?
經過嘗試,發現union注入也是可以的!根本不需要進行多句執行!
實際上,在模擬預編譯的情況下,PDO對于SQL注入的防范(PDO::queto()),無非就是將數字型的注入轉變為字符型的注入,又用類似mysql_real_escape_string()的方法將單引號、雙引號、反斜杠等字符進行了轉義。
這種防范方法在GBK編碼的情況下便可用寬字節進行繞過,而在非GBK編碼的情況下,若存在二次注入的情況,是否能利用呢?
答案是否定的。
二次注入是由于對添加進數據庫中的數據沒有再次處理和轉義而導致的,而預編譯對每次查詢都進行轉義,則不存在二次注入的情況。
上述安全隱患,是由于未正確設置PDO造成的,在PDO的默認設置中,PDO::ATTR_EMULATE_PREPARES和PDO::MYSQL_ATTR_MULTI_STATEMENTS都是true,意味著模擬預編譯和多句執行是默認開啟的。
而在非模擬預編譯的情況下,若語句中沒有可控參數,是否還能這樣做呢?
答案是否定的。
我們將PDO::ATTR_EMULATE_PREPARES設為false,來看看sql語句到底執行了什么:
它對每一句sql語句都進行了預編譯和執行兩個操作,在執行select balabala from table1 where 1=?這句時,如果是GBK編碼,那么它將會把?綁定的參數轉化成16進制,這樣無論輸入什么樣的東西都無法再進行注入了。
如果不是GBK編碼,如上面所說,也不存在二次注入的情況,故可以避免SQL注入漏洞。
PDO的原理,與Mysql中prepare語句是一樣的。上面PDO所執行的SQL語句,用如下的方式可以等效替代:
Set @x=0x31 Prepare a from “select balabala from table1 where 1=?” Execute a using @x
我們可以手動將輸入的參數設置為@x,并將其轉化為16進制,隨后預編譯,再執行
也就是說,不用PDO也可以仿照其原理手動設置預編譯:
$db = new mysqli('localhost','root','','pdotest'); if(isset($_GET['id'])) { $id = "0x".bin2hex($_GET['id']); } else { $id=1; } echo "id:".$id."</br>"; $db->query("set names gbk"); $db->query("set @x={$id}"); $db->query("prepare a from 'select balabala from table1 where 1=?'"); $row = $db->query("execute a using @x"); $result = $row->fetch_assoc(); if($result) { echo "結果為:"; print_r($result); echo "</br>"; }
得到的結果和使用PDO是一樣的:
這樣設置不用擔心沒有合理地設置PDO,或是用了GBK編碼等情況。
Prepare語句在防范SQL注入方面起到了非常大的作用,但是對于SQL注入攻擊卻也提供了新的手段。
Prepare語句最大的特點就是它可以將16進制串轉為語句字符串并執行。如果我們發現了一個存在堆疊注入的場景,但過濾非常嚴格,便可以使用prepare語句進行繞過。
例如我們將createtable table2 like table1轉化成16進制,然后執行:
我們發現數據庫中已經多了一個表table2。則語句成功執行了。
對于此類問題的防范,主要有以下三個方面:
1. 合理、安全地使用gbk編碼。即使采用PDO預編譯的方式,如若配置不當,依然可造成寬字節注入
2. 使用PDO時,一定要將模擬預編譯設為false
3. 可采用使用Prepare Statement手動預編譯,杜絕SQL注入
上述就是小編為大家分享的如何從SQL寬字節注入認識的PDO原理和正確使用了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。