資料庫預編譯為何能防止SQL注入?

PHP官方文檔看了2遍多,還是不太理解預編譯的意義?為何通過PDO等預編譯手段能阻止用戶惡意拼接SQL語句?就PHP而言,PDO從更底層而言是如何調用資料庫查詢流程的,和一般的mysql_query()區別在哪裡?請教一下各位大牛,謝謝!


因為prepare是把SQL模板發給MySQL編譯,然後execute是把用戶輸入的參數交給MySQL套模板執行.PHP模擬預處理的意思就是資料庫並不會有編譯模板這一步,而是PHP自動轉義用戶輸入的參數,相當於手動調用mysqli_real_escape_string($_GET["id"]) 或 PDO::quote($_GET["id"]),比如PHP的一個Query Builder庫medoo.php用的就是PDO::quote轉義用戶輸入的參數後query執行SQL.

WireShark里用tcp.port==3306過濾分析PHP和MySQL通信可見:

但並非所有資料庫都支持預處理,像SQLite就不支持.
所以PHP提供了模擬預處理(默認開啟),其本質是轉義用戶輸入,相關函數是:
PDO::quote和mysqli_real_escape_string.
在模擬預處理下,綁定參數(bindParam/bind_param)本質也是轉義,而非SQL模板和參數分離.

安全的轉義依賴統一的編碼:
http://php.net/mysqli_real_escape_string
http://php.net/manual/zh/mysqlinfo.concepts.charset.php
MySQLi中需要先用 $mysqli-&>set_charset("utf8") 定義字符集後,
才能確保 $mysqli-&>real_escape_string() 能夠安全正確轉義用戶輸入的字元串.
PDO_MySQL中需要在PDO()中用 charset=utf8 定義字符集後,
才能確保 PDO::quote() 能夠安全正確轉義用戶輸入的字元串.
PHP模擬預處理時,PDO的bindParam/bindValue/execute轉義時底層用的應該也是PDO::quote().


因為 preparedStatement 中可以不包含數據,只包含操作,不需要用數據來拼接 SQL。


畢竟不清楚數據內部的實現原理,只能自己找一種合理的解釋。資料庫執行sql語句可以理解為按照關鍵字來編譯sql語句,而預編譯後資料庫肯定就不再重複編譯了,所以這時再傳什麼 AND, OR都只會當成普通字元串來處理。


剛好這兩天在學習這塊的知識

《WEB應用安全權威指南》中是這樣講佔位符的

書中附註中還有一句話,靜態佔位符在ISO或JIS中,也被稱為預處理語句(Prepared Statement)。

其中提到綁定變數時字面量會被妥善處理指的是資料庫引擎會將變數中的字元進行轉義處理。

這有一篇關於資料庫將變數進行轉義的博文(是Java的處理方法),不過他轉義後的字元串寫錯了,在博文的評論里有人指出了正確的轉義後字元串。

佔位符,SQL注入? - 越國岩的專欄 - 博客頻道 - CSDN.NET

書中後面也給了安全連接方法的參考

其餘內容可參考這本書的相關章節。


可以理解為先把茅坑挖好了,再把屎包到袋裡扔進去,這樣你就拉不到外邊了


。。。官方文檔寫的很清楚啊


推薦閱讀:

連續簽到獎勵 資料庫如何設計?
怎麼樣從 web 開發人員轉成 DBA ?
數據量很大,邏輯不能在內存里做怎麼辦?
高並發insert ignore是選擇innodb還是myisam?
innodb的意向鎖有什麼作用?

TAG:資料庫 | PHP | 網路安全 | SQL注入 | 安全開發周期 |