PHP+MySQL 環境下 SQL Injection 攻防戰

OurMySQL Blog 看到這篇:『PHP+MySQL环境下SQL Injection攻防总结』寫的相當不錯,裡面有一些觀念,可以讓初學 PHP & MySQL 的使用者知道如何防護 SQL Injection (資料隱碼),內容提到 magic_quotes_gpc 在 on 跟 off 的狀況如何防護,但是可以清楚看到 PHP 官方文件提到在 PHP 5.3.0 magic_quotes_gpc 預設已經是關閉,在 PHP 6.0 之後正式移除,所以內容寫的 magic_quotes_gpc 狀況,可以大致上瞭解就好,真正防護 SQL Injection 是需要寫程式或者是考慮很多方式去防護。 一般在做文章查詢,都會使用 /articles.php?id=123 網址傳送方式,以 $_GET[‘id’] 送到 PHP 頁面去做處理,如果駭客想要測試是否可以利用 SQL Injection 做攻擊,可以在網址列加上 /articles.php?id=123,請注意網址後面多出 ,如果沒有把 $_GET[‘id’] 做處理的話,就會出現底下錯誤訊息:

supplied argument is not a valid MySQL result resource in 這是因為平常在寫 SQL 語法,會是底下這種寫法:

$sql = "SELECT id, title, content FROM articles WHERE id = '".$_GET['id']."'";
$result = mysq_query($sql);
因為沒有處理跳脫字元 ',所以造成 SQL 語法錯誤,才會出現該錯誤訊息,但是如果又針對跳脫字元做防護得時候,還有另一種攻擊方式:

/articles.php?id=0 union select 1,2,load_file(char(47,101,116,99,47,112,97,115,115,119,100)) 其中的數字就是/etc/passwd 字符串的ASCII,除此之外,還可以使用字串 16 進位方式:

/articles.php?id=0 union select 1,2,load_file(0×2f6574632f706173737764) 可以參考一下 MySQL LOAD_FILE(file_name),底下文章提到了很多方式解決。

1:字串過濾可以使用

intvalfloatval、或者是 settype 等函式來處理。 2:MySQL 執行 SQL command 的時候,一律用 mysql_real_escape_string 方式過濾字串 3:選擇一套好的處理 MySQL Query 的套件,盡量不要自己寫,可以參考各大 Open Source, ex: phpBBPDO 的 prepare 處理 4:使用 apache mod_rewrite 功能,將網址隱藏,或者是編碼 5:將錯誤提示關閉:display_errors=off,不過個人不建議這個做啦。 6:MySQL 權限設定,不要將有 File 權限(ex: root)的使用者設定在網站使用。 針對第二點寫一個 PHP 過濾:

if( is_array($_POST) && !get_magic_quotes_gpc())
{
	while( list($k, $v) = each($_POST) )
	{
		$$k = mysql_real_escape_string(trim($v));
	}
	@reset($_POST);
}
function set_var(&$result, $var, $type, $multibyte = false)
{
	settype($var, $type);
	$result = $var;
  
	if ($type == 'string')
	{
		$result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8'));
		if (!empty($result))
		{
			// Make sure multibyte characters are wellformed
			if ($multibyte)
			{
				if (!preg_match('/^./u', $result))
				{
					$result = '';
				}
			}
			else
			{
				// no multibyte, allow only ASCII (0-127)
				$result = preg_replace('/[\x80-\xFF]/', '?', $result);
			}
		}

		$result = (STRIP) ? stripslashes($result) : $result;
	}
	
}
php 

See also