SQL 注入攻击相当容易防范,但是许多应用仍然容易受到这种攻击的侵害。考虑下面的 SQL 语句:
<?php
$sql = "INSERT
INTO users (reg_username,
reg_password,
reg_email)
VALUES ('{$_POST['reg_username']}',
'$reg_password',
'{$_POST['reg_email']}')";
?>
这个语句由 $_POST 构成,这是毫无疑问的。
假设这个语句创建了一个新帐号。用户提供了想要的用户名和邮件帐号。注册程序产生临时的密码并发送邮件到用户处用于验证邮件地址。设想用户输入下面的内容作为用户名:
bad_guy', 'mypass', ''), ('good_guy
这当然不是一个合法的用户名,但是关键的地方没有数据过滤,应用程序无法判断。如果提供一个合法的邮件地址(例如 shiflett@php.net),而应用程序生成密码为 1234,SQL 语句将变为:
<?php
$sql = "INSERT
INTO users (reg_username,
reg_password,
reg_email)
VALUES ('bad_guy', 'mypass', ''), ('good_guy',
'1234',
'shiflett@php.net')"; ?>
除了用合法的邮件地址创建了一个帐号(good_guy),应用程序还创建了第二个帐号(bad_guy),用户提供了所有关于这个帐号的信息。
虽然这个特殊的例子看起来并没有什么很大的伤害,不过一旦攻击者可以修改 SQL 语句会造成多么大的损失也是相当明确的。
例如,相对于使用的数据库,可能允许一次向数据库服务器发送多个查询。因此,用户可以用分号终结当前查询,并且在其后跟随用户定义的查询。
新版本的 MySQL 允许多个查询,但是需要对应的 PHP 扩展(ext/mysqli)中的特定函数来发送多个查询(mysqli_multi_query() 来代替
mysqli_query())。只允许单一查询是更安全的,因为这限制了攻击者可能会做的事情。
防范 SQL 注入是很简单的:
过滤数据。
这并不是罗唆。在合适的地方使用良好的数据过滤,可以减小多数安全隐患,甚至可以消除其中的一部分。
将数据用括号包含。
如果你的数据库允许(MySQL 允许),在 SQL 语句中,不论什么类型的数据都用单引号包含起来。
转义数据。
一些合法的数据可能在无意中破坏 SQL 语句本身的格式。使用 mysql_escape_string() 或者所使用数据库提供的转移函数。如果没有提供这样的函数,addslashes() 也是不错的最后选择。