SQL 注入
如果你通过网页获取用户输入的信息并将其插入SQL数据库,你就有可能为一个被称为SQL注入的安全问题敞开大门。
本章将教你如何防止这种情况的发生,并帮助保护服务器端代码中的SQL语句。
注入通常发生在你要求用户输入的时候,比如他们的名字,而他们给你的是一个SQL语句,你会在不知不觉中在数据库中运行。永远不要相信用户提供的数据,只有在验证后才能处理这些数据;作为一项规则,这是由模式匹配完成的。
在下面的例子中,name限制为字母数字字符加下划线,长度为 8 到 20 个字符(根据需要修改这些规则)。
if (preg_match("/^\w{8,20}$/", $_GET['username'], $matches)) { $result = mysql_query("SELECT * FROM CUSTOMERS WHERE name = $matches[0]"); } else { echo "user name not accepted"; }
为了演示这个问题,请考虑以下摘录:
// 假设输入 $name = "Qadir'; DELETE FROM CUSTOMERS;"; mysql_query("SELECT * FROM CUSTOMSRS WHERE name='{$name}'");
该函数调用应该从 CUSTOMERS 表中检索一条记录,其中名称列与用户指定的名称相匹配。在正常情况下,$name将只包含字母数字字符和空格,例如字符串 ilia。但是在这里,通过向 $name 附加一个全新的查询,对数据库的调用变成了灾难;注入的 DELETE 查询从 CUSTOMERS 表中删除所有记录。
幸运的是,如果你使用 MySQL,mysql_query()函数不允许在单个函数调用中执行多个 SQL 查询,如果你尝试多次查询,调用将失败。
但是,其他 PHP 数据库扩展,例如SQLite和PostgreSQL将愉快地执行多次查询,执行一个字符串中提供的所有查询将会造成严重的安全问题。
防止 SQL 注入
你可以在 PERL 和 PHP 等脚本语言中巧妙地处理所有转义字符,PHP 的 MySQL 扩展提供了功能mysql_real_escape_string()转义 MySQL 特有的输入字符。
if (get_magic_quotes_gpc()) { $name = stripslashes($name); } $name = mysql_real_escape_string($name); mysql_query("SELECT * FROM CUSTOMERS WHERE name='{$name}'");
LIKE 困境
为了解决 LIKE 困境,自定义转义机制必须将用户提供的 '%' 和 '_' 字符转换为文字,用addcslashes()函数可让你指定要转义的字符范围。
$sub = addcslashes(mysql_real_escape_string("%str"), "%_"); // $sub == \%str\_ mysql_query("SELECT * FROM messages WHERE subject LIKE '{$sub}%'");