標籤:mysql sql
SQL 注入( SQL Injection )攻擊是發生在應用程式中的資料庫層的安全性漏洞。簡而言之,是在輸入的字串之中注入 SQL 陳述式,如果在設計不良的程式中忽略了檢查,那麼這些注入進去的 SQL 陳述式就會被資料庫伺服器誤認為是正常的 SQL 陳述式而運行,攻擊者就可以執行計畫外的命令或訪問未被授權的資料。 SQL 注入已經成為互連網世界 Web 應用程式的最大風險。我們有必要從開發、測試、上線各個環節對其進行防範。以下將介紹 SQL 注入的原理及如何預防 SQL 注入。
SQL 注入的原理有如下 4 點
1 )拼接惡意查詢。 SQL 命令可查詢、插入、更新、刪除資料,以分號字元分隔不同的命令。
例如:
select * from users where user_id = $user_id
user_id 是傳入的參數,如果傳入了 “1234;delete from users” ,那麼最終的查詢語句會變為:
select * from users where user_id = 1234; delete from users
如上語句如果執行,則會刪除 user 表的資料。
2 )利用注釋執行非法命令。 SQL 命令中,可以插入注釋。
例如:
select count(*) as ‘num‘ from game_score where game_id=24411 and platform_id=11 and version=$version and session_id =
sessid=‘d7a157-0f-48b6-98-c35592‘
如果 version 包含了惡意的字串 “‘-1‘OR 3 AND SLEEP(500)--” ,那麼最終查詢語句會變成下面這個樣子:
select count(*) as ‘num‘ from game_score where game_id=24411 and platform_id=11 and version=‘-1‘ OR 3 AND SLEEP(500)-- ‘
and session_id = sessid=‘d7a157-0f-48b6-98-c35592‘
以上惡意查詢只是想耗盡系統資源, SLEEP(500) 將導致 SQL 一直運行,如果添加了修改、刪除資料的惡意指令,將會造成
更大的破壞。
3 ) SQL 命令對於傳入的字串參數是用單引號引起來的。如果字串本身包含單引號而沒有被處理,則可能會篡改原本
的 SQL 文法的作用。
例如:
select * from user_name where user_name = $user_name
如果 user_name 傳入的是 G‘chen ,那麼最終的查詢語句會變成這樣:
select * from user_name where user_name =‘G‘chen‘
以上語句將會出錯,這樣的語句風險比較小,因為語法錯誤的 SQL 陳述式不會被執行。但也可能惡意產生的 SQL 陳述式,沒有
任何語法錯誤,並且以一種你不期望的方式運行。
4 )添加一些額外的條件為真值運算式,改變執行行為。
例如:
update users set userpass=SHA2(‘$userpass‘) where user_id=$user_id;
如果 user_id 被傳入惡意的字串 “1234 OR TRUE” ,最終的 SQL 陳述式會變成下面這樣:
update users set userpass=SHA2(‘123456‘) where user_id=1234 OR TRUE;
這將更改所有使用者的密碼。
下面是避免 SQL 注入的一些方法。
( 1 )過濾輸入內容,校正字串
應該在將資料提交到資料庫之前,就把使用者輸入中的不合法字元剔除掉。可以使用程式設計語言提供的處理函數,如 PHP 的
mysql_real_escape_string() 來剔除,或者定義自己的處理函數進行過濾,還可以使用Regex匹配安全的字串。
如果值屬於特定的類型或有約定的格式,那麼在拼接 SQL 陳述式之前就要進行校正,驗證其的有效性。比如對於某個傳入的
值,如果可以確定是整型,那麼我們要判斷下它是否為整型,不僅在瀏覽器端(用戶端),而且在伺服器端也需要進行驗證。
( 2 )參數化查詢
參數化查詢目前已被視作是最有效預防 SQL 插入式攻擊的方法。不同於在 SQL 陳述式中插入動態內容,查詢參數的做法是在
準備查詢語句的時候,就在對應參數的地方使用參數預留位置。然後,在執行這個預先準備好的查詢時提供一個參數。
在使用參數化查詢的情況下,資料庫伺服器不會將參數的內容視為 SQL 指令的一部分來進行處理,而是在資料庫完成 SQL
指令的編譯之後,才套用參數運行,因此就算參數中含有破壞性的指令,也不會被資料庫所運行。
可以使用 MySQLi 擴充或 pdo 擴充來綁定參數實現參數化查詢。
如下是一個使用 MySQLi 擴充綁定參數的樣本。
<html>
<head>
<title> test parameter query </title>
</head>
<body>
<?php
$host="127.0.0.1";
$port=3306;
$socket="";
$user="garychen";
$password="garychen";
$dbname="employees";
$con = new mysqli($host, $user, $password, $dbname, $port, $socket)
or die (‘Could not connect to the database server‘ . mysqli_connect_error());
echo ‘connect to employees database successfully‘;
echo "<br />";
echo "select departments table using parameter";
echo "<br />";
$query = "select * from departments where dept_name = ?";
if ($stmt = $con->prepare($query)) {
$stmt->bind_param("s",$depname);
$depname="Finance";
$stmt->execute();
$stmt->bind_result($field1, $field2);
while ($stmt->fetch()) {
printf("%s, %s\n", $field1, $field2);
echo "<br />";
}
$stmt->close();
}
$con->close();
?>
</body>
</html>
上例首先是預先處理語句 if($stmt=$con->prepare($query)) ,然後綁定參數使用 bind_param() 方法,該方法的文法格式如下所示。
bool mysqli_stmt::bind_param ( string $types , mixed &$var1 [, mixed &$... ] )
其中, types 指定綁定參數的類型,包含了一個或多個字元。 I 代表整型, D 代表雙精確度, S 代表字串, B 代表 BLOB 類型,
本例中是 S 。
但是綁定參數也有如下一些限制。
· 不能讓預留位置 “?” 代替一組值,例如:
SELECT * FROM departments WHERE userid IN ( ? );
· 不能讓預留位置 “?” 代替資料表名或列名,例如:
SELECT * FROM departments ORDER BY ?;
· 不能讓預留位置 “?” 代替 SQL 關鍵字,例如:
SELECT * FROM departments ORDER BY dept_no ?;
對於 Java 、 JSP 開發的應用,也可以使用預先處理語句加綁定參數的方式來避免 SQL 注入。
( 3 )安全性測試、安全審計
除了開發規範,還需要流程、機制和合適的工具來確保代碼的安全。我們應該在開發過程中對代碼進行審查,在測試環節
使用工具進行掃描,上線後定期掃描安全性漏洞。通過多個環節的檢查,一般是可以避免 SQL 注入的。
有些人認為預存程序可以避免 SQL 注入,預存程序在傳統行業裡用得比較多,對於許可權的控制是有一定用處的,但如果存
儲過程用到了動態查詢,拼接 SQL ,那就一樣會存在安全隱患。一些編程架構對於寫出更安全的代碼也有一定的協助,因為它
提供了一些處理字串的函數和使用查詢參數的方法,但同樣,你仍然可以編寫出不安全的 SQL 陳述式。所以歸根到底
摘自MySQLDBA修鍊之道
本文出自 “大李子” 部落格,謝絕轉載!
mysql 之sql注入詳解