如果通過網(wǎng)頁接收用戶輸入,而后再把這些數(shù)據(jù)插入到數(shù)據(jù)庫中,那么你可能就會碰到 SQL 注入式攻擊。本節(jié)簡要介紹如何防范這種攻擊,確保腳本和 MySQL 語句的安全性。
注入式攻擊往往發(fā)生在要求用戶輸入時,比如說要求他們輸入自己的名字,但是他們卻輸入了一段 MySQL 語句,不知不覺地運行在數(shù)據(jù)庫上。
永遠(yuǎn)不要相信用戶所提供的數(shù)據(jù),只有在驗證無誤后,才能去處理數(shù)據(jù)。通常,利用模式匹配來實現(xiàn)。在下面這個范例中,username(用戶名)被限定為字母數(shù)字混合編制的字符串,再加上下劃線,長度限定為8~20個字符之間。當(dāng)然,可以按需要修改這些規(guī)范。
if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches))
{
$result = mysql_query("SELECT * FROM users
WHERE username=$matches[0]");
}
else
{
echo "username not accepted";
}
為了暴露問題所在,請考慮下面這段代碼:
// 本應(yīng)該的輸入
$name = "Qadir'; DELETE FROM users;";
mysql_query("SELECT * FROM users WHERE name='{$name}'");
函數(shù)調(diào)用原本會從 users 表中獲取一個記錄。name 列與用戶所指定的名字所匹配。在一般情況下,$name 會包含字母數(shù)字混合編制的字符,或許還包含空格,such as the string ilia. 但這里,為 $name 添加了一個全新的查詢,數(shù)據(jù)庫調(diào)用就變成了災(zāi)難:注入的 DELETE 查詢會刪除 users 表中所有的記錄。
幸運的是,如果使用 MySQL,mysql_query() 函數(shù)不允許堆疊查詢,或在一個函數(shù)調(diào)用中執(zhí)行多個查詢。如果嘗試使用堆疊查詢,則調(diào)用會失敗。
然而,有些 PHP 數(shù)據(jù)庫擴展,比如 SQLite 或 PostgreSQL,卻能很好地執(zhí)行堆疊查詢,能夠執(zhí)行一個字符串中所提供的所有查詢,從而造成嚴(yán)重的安全隱患。
使用 PERL 或 PHP 這樣的腳本語言,可以很巧妙地處理轉(zhuǎn)義字符。MySQL 針對 PHP 的擴展也提供了 mysql_real_escape_string() ,可以將輸入的字符轉(zhuǎn)義為MySQL所特有的字符。
if (get_magic_quotes_gpc())
{
$name = stripslashes($name);
}
$name = mysql_real_escape_string($name);
mysql_query("SELECT * FROM users WHERE name='{$name}'");
為了解決 LIKE 困境,常用的轉(zhuǎn)義機制必須將 用戶所輸入的 % 和 _ 字符轉(zhuǎn)義為字面值。使用 addcslashes() 能為你指定一個轉(zhuǎn)義字符范圍。
$sub = addcslashes(mysql_real_escape_string("%something_"), "%_");
// $sub == \%something\_
mysql_query("SELECT * FROM messages WHERE subject LIKE '{$sub}%'");