關於使用佔位符來解決SQL注入
阿新 • • 發佈:2019-02-11
總結:
SQL已經預編譯好了,然後替換中間的佔位符,這個佔位符在編譯後就已經確定了它只是一個引數屬性。因此,用注入的程式碼去替換佔位符,這個SQL也不會再進行編譯了,所以也達不到注入的目的。
SQL注入並不是一個在SQL內不可解決的問題,這種攻擊方式的存在也不能完全歸咎於SQL這種語言,因為注入的問題而放棄SQL這種方式也是因噎廢食。首先先說一個我在其他回答中也曾提到過的觀點:沒有(執行時)編譯,就沒有注入。
SQL注入產生的原因,和棧溢位、XSS等很多其他的攻擊方法類似,就是未經檢查或者未經充分檢查的使用者輸入資料,意外變成了程式碼被執行。針對於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