動態查詢便是指將變量放到語句中,將固定的字符串和變量拼接在一塊兒,組成一個完整的SQL查詢語句,因爲變量的值是動態變化的,所以查詢也是動態的。編寫這樣的可以執行的動態的查詢語句是十分天然的,也很是方便。可是,不經思考的加入也會帶來很大的安全隱患。例如這樣的一個查詢:php
SELECT × FROM Bugs WHERE bug_id = $bug_id";
若是這個時候$bug_id = "1234; DELETE FROM Bugs",Bugs表就跪了。所以SQL注入的危害至關大,不能將未經驗證的輸入做爲代碼執行。mysql
沒有一種技術可以徹底的抵抗SQL注入,所以,須要將多種作法結合起來正則表達式
轉移能防止一些意外的狀況,例如這個查詢:sql
SELECT * FROM Projects WHERE project_name = '$project_name'
若是$project_name = O'Hare(這是很正常的一種狀況),因爲名字裏面有個引號,查詢就變成:數據庫
SELECT * FROM Projects WHERE project_name = 'O'Hare'
這個時候數據庫就會由於多了個引號而出錯,所以須要在名字裏面的'前加反斜槓。數組
參數化查詢是指在編寫查詢語句的時候,在須要參數的位置上使用參數佔位符,而後查詢的時候提供這一參數。以php爲例:安全
<?php $sql = "SELECT * FROM Projects WHERE project_name = ?"; $stmt = mysqli_prepare($con, $sql); mysqli_stmt_bind_param($stmt, 's', $_POST['name']); mysqli_stmt_execute($stmt);
簡單來講,數據庫接到一個指令,大體是這麼作的:函數
編譯SQL生成執行計劃優化
選擇執行計劃code
執行
SQL注入不少狀況是增長查詢語句,增長查詢語句就會形成查詢的語義發生變化(就是說編譯會生成不一樣的東西)。若是使用參數化查詢,在準備查詢語句的時候數據庫就去編譯,而後提供參數就會重用這個編譯結果,即便使用了注入變量,語義也不會發生變化(不會從新編譯),同時,傳參的時候,數據庫也會將變量進行過濾,以達到防止注入的效果。
參數化查詢也有作不到的東西:
1.多個值的列表不能做爲單一參數
若是編寫這麼一個查詢:
SELECT * FROM Bugs WHERE bug_id IN (?)
那麼你傳遞參數的時候不能是「1234,3456,5678」這樣的,這樣會被認爲是一個字符串
2.表名,列名,SQL關鍵字不能做爲參數
應該將全部不合法的字符去掉,而不是找是否有些輸入包含了危險的內容。
好比說若是是須要一個整數,就應該只是用變量裏面的整數部分,(在Php裏面有filter擴展),能夠直接用類型轉換函數,也能夠用正則表達式匹配
通常都用參數化查詢,可是有時候,參數化查詢會致使數據庫採用錯誤的優化方案(多是採用錯誤的索引)。好比說表裏面有一列99%的都是TRUE,那麼用因此很快就能查詢到那1%的FALSE。這個時候將變量直接插進去是好的作法。但這個時候就要格外當心的引用字符串。在php裏面可使用PDO::quote()
好比說,你想要讓用戶選擇究竟是按什麼關鍵字排序,是升序排序,仍是按降序排序,這個時候你從客戶端接受兩個參數
<?php $sort_standard = $_POST['order']; $direction = $_POST['direction']; $sql = "SELECT * FROM Bugs ORDER BY $sort_standard $direction"; mysqli_query($con, $sql);
可是這個並非一個安全的作法,由於用戶能夠傳遞任意值。可是參數化查詢又不能是關鍵字,採用這樣的作法:
先聲明$sort_standard和$direction數組,好比這樣
$sort_standard = array( "status" => "status", "date" => "date_report" ); $direction = array( "up" => "ASC", "down" => "DESC" );
當判斷到用戶的選擇不在數組中的時候就使用默認值,若是在數組中就用對應值,這樣作:
從不使用用戶的輸入進行查詢,減小了注入風險
讓語句動態化,而沒有語法上的限制
將數據庫查詢和用戶界面解藕