(17)sql注入與sql mode

林灣村龍貓發表於2017-01-20

概述

  sql注入就是利用某些資料庫的外接介面將使用者資料插入到實際的資料庫操作語言當中,從而達到入侵資料庫乃至作業系統的目的。在安全領域,我們永遠不要信任使用者的輸入,我們必須認定使用者輸入的資料都是不安全的,我們都需要對使用者輸入的資料進行過濾處理。沒有(執行時)編譯,就沒有注入。所以從根本上防止上述型別攻擊的手段,還是避免資料變成程式碼被執行,時刻分清程式碼和資料的界限。而具體到SQL隱碼攻擊來說,被執行的惡意程式碼是通過資料庫的SQL解釋引擎編譯得到的,所以只要避免使用者輸入的資料被資料庫系統編譯就可以了。
  與其他資料庫不同,MySQL可以執行在不同的SQL Mode(SQL伺服器模式)下,並且可以為不同客戶端應用不同模式。這樣每個應用程式可以根據自己的需求來定製伺服器的操作模式。模式定義MySQL應支援哪些SQL語法,以及應執行哪種資料驗證檢查。這有點類似於apache配置不同級別的錯誤日誌,報告哪些錯誤,又不報告哪些錯誤。

SQL隱碼攻擊

1.注入例項

//php程式碼
$unsafe_variable = $_POST['user_input'];   
mysql_query("INSERT INTO `table` (`column`) VALUES ('{$unsafe_variable}')");複製程式碼

當post中程式碼如下時候:

value'); DROP TABLE table;--複製程式碼

查詢程式碼變成

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')複製程式碼

這樣會直接刪除table表,你的資料被破壞了。

2.防止sql注入

方法一
prepareStatement+Bind-Variable:SQL語句和查詢的引數分別傳送給資料庫伺服器進行解析。
對於php來說有兩種實現方式。

//使用PDO(PHP data object)
$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');  
$stmt->execute(array('name' => $name));  
foreach ($stmt as $row) {  
    // do something with $row  
}

//使用mysql擴充套件-mysqli
$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
    // do something with $row
}複製程式碼

方式二
對查詢語句進行轉義(最常見的方式):使用應用程式提供的轉換函式。
|應用|函式|
|--------|
|MySQL C API|mysql_real_escape_string()|
|MySQL++|escape和quote修飾符|
|PHP|使用mysql_real_escape_string()(適用於PHP4.3.0以前),之後可以使用mysqli或pdo|
|Perl DBI|placeholder或quote()|
|Ruby DBI|placeholder或quote()|

方式三
使用自己定義函式進行校驗:其本質上還是對輸入非法資料進行轉義和過濾。
輸入驗證可以分為:1.整理資料使之有效;2.拒絕已知的非法輸入;3.只接收已知的合法輸入。

方式四
使用儲存過程。
儲存過程參見: (9)mysql中的儲存過程和自定義函式

sql伺服器模式

1.sql模式語法

#檢視當前sql模式
select @@sql_mode;
#檢視當前sql模式
SELECT @@session.sql_mode;
#修改當前sql模式
SET [SESSION][GLOBAL] sql_mode='modes';複製程式碼
  • 其中session選項表示只在本次連線生效;而global表示在本次連線不生效,下次連線生效。
  • 也可以使用“--sql-mode='modes'”,在MySQL啟動時候設定sql_mode。
  • 可以在配置檔案中設定。
    ###2.sql_mode常用值
    ONLY_FULL_GROUP_BY:
    對於GROUP BY聚合操作,如果在SELECT中的列,沒有在GROUP BY中出現,那麼這個SQL是不合法的,因為列不在GROUP BY從句中。

NO_AUTO_VALUE_ON_ZERO:
該值影響自增長列的插入。預設設定下,插入0或NULL代表生成下一個自增長值。如果使用者 希望插入的值為0,而該列又是自增長的,那麼這個選項就有用了。

STRICT_TRANS_TABLES:
在該模式下,如果一個值不能插入到一個事務表中,則中斷當前的操作,對非事務表不做限制。

NO_ZERO_IN_DATE:
在嚴格模式下,不允許日期和月份為零。

NO_ZERO_DATE:
設定該值,mysql資料庫不允許插入零日期,插入零日期會丟擲錯誤而不是警告。

ERROR_FOR_DIVISION_BY_ZERO:
在INSERT或UPDATE過程中,如果資料被零除,則產生錯誤而非警告。如 果未給出該模式,那麼資料被零除時MySQL返回NULL。

NO_AUTO_CREATE_USER:
禁止GRANT建立密碼為空的使用者。

NO_ENGINE_SUBSTITUTION:
如果需要的儲存引擎被禁用或未編譯,那麼丟擲錯誤。不設定此值時,用預設的儲存引擎替代,並丟擲一個異常。

PIPES_AS_CONCAT:
將"||"視為字串的連線操作符而非或運算子,這和Oracle資料庫是一樣的,也和字串的拼接函式Concat相類似。

ANSI_QUOTES:
啟用ANSI_QUOTES後,不能用雙引號來引用字串,因為它被解釋為識別符。

說明

ORACLE的sql_mode設定等同:PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER。

參考

1.sql注入

www.zhihu.com/question/22…
blog.csdn.net/agoago_2009…

2.sql模式

tech.it168.com/a2012/0822/…
c.biancheng.net/cpp/html/14…