1. 程式人生 > >關於使用佔位符來解決SQL注入

關於使用佔位符來解決SQL注入

總結:

SQL已經預編譯好了,然後替換中間的佔位符,這個佔位符在編譯後就已經確定了它只是一個引數屬性。因此,用注入的程式碼去替換佔位符,這個SQL也不會再進行編譯了,所以也達不到注入的目的。

SQL注入並不是一個在SQL內不可解決的問題,這種攻擊方式的存在也不能完全歸咎於SQL這種語言,因為注入的問題而放棄SQL這種方式也是因噎廢食。首先先說一個我在其他回答中也曾提到過的觀點:沒有(執行時)編譯,就沒有注入


SQL注入產生的原因,和棧溢位、XSS等很多其他的攻擊方法類似,就是未經檢查或者未經充分檢查使用者輸入資料意外變成了程式碼被執行。針對於SQL注入,則是使用者提交的資料,被資料庫系統編譯而產生了開發者預期之外的動作。也就是,SQL注入是使用者輸入的
資料,在拼接SQL語句的過程中,超越了資料本身,成為了SQL語句查詢邏輯的一部分,然後這樣被拼接出來的SQL語句被資料庫執行,產生了開發者預期之外的動作。

所以從根本上防止上述型別攻擊的手段,還是避免資料變成程式碼被執行,時刻分清程式碼和資料的界限。而具體到SQL注入來說,被執行的惡意程式碼是通過資料庫的SQL解釋引擎編譯得到的,所以只要避免使用者輸入的資料被資料庫系統編譯就可以了。

現在的資料庫系統都提供SQL語句的預編譯(prepare)和查詢引數繫結功能,在SQL語句中放置佔位符'?',然後將帶有佔位符的SQL語句傳給資料庫編譯,執行的時候才將使用者輸入的資料作為執行的引數傳給使用者。這樣的操作不僅使得SQL語句在書寫的時候不再需要拼接,看起來也更直接,而且使用者輸入的資料也沒有機會被送到資料庫的SQL直譯器被編譯執行,也不會越權變成程式碼。


至於為什麼這種引數化的查詢方式沒有作為預設的使用方式,我想除了相容老系統以外,直接使用SQL確實方便並且也有確定的使用場合。

多說一點,從程式碼的角度來看,拼接SQL語句的做法也是不恰當的。
DELETE FROM planet WHERE name = 'mercury';
DELETE FROM planet WHERE name = 'venus';
DELETE FROM planet WHERE name = 'earth';
DELETE FROM planet WHERE name = 'mars';
我修改一下那個很經典的笑話:程式設計師不應該執行刪除地球這樣的SQL語句,而是寫
刪除一個行星,然後將地球當作引數傳入。
$stmt = $mysqli->prepare("DELETE FROM planet WHERE name = ?");
$stmt->bind_param('s', "earth");
$stmt->execute();


更多閱讀:
MySQL :: MySQL 5.1 Reference Manual :: 13.5 SQL Syntax for Prepared Statements
Prepared statement