如果通过网页需要用户输入一些数据信息,并将其插入到MySQL数据库,这是一个引入SQL注入安全问题的机会。这一节将学习如何防止这种情况的发生,并帮助保护脚本和MySQL语句。
通常注入是在当要求用户输入时,类似他们的姓名,只是一个名字,他们给出,会在不知不觉中包含MySQL的语句会在数据库运行。
永远不要信任用户提供的数据,这个过程只有在数据验证后,作为一项规则,这是通过模式匹配进行。在下面的例子中,用户名被限制在字母+数字+字符加下划线,并在8-20个字符之间的长度 - 可以根据需要修改这些规则。
if (preg_match("/^w{8,20}$/", $_GET['username'], $matches)) { $result = mysql_query("SELECT * FROM users WHERE username=$matches[0]"); } else { echo "username not accepted"; }
为了说明问题,考虑这个片段:
// supposed input $name = "Qadir'; DELETE FROM users;"; mysql_query("SELECT * FROM users WHERE name='{$name}'");
该函数调用从表中检索用户记录,其中名称列匹配由用户指定的名称。 在正常情况下,$name将只包含字母数字字符,或可能是空格,如字符串ilia。 但在这里,通过附加一个全新的查询到$name,在调用数据库变成灾难:注入DELETE查询删除所有的用户记录。
幸运的是,如果使用MySQL,mysql_query()函数不允许查询堆叠或一个函数调用执行多个查询。如果尝试堆叠查询,调用失败。
然而,其他PHP数据库扩展,如SQLite和PostgreSQL,它们会乐意地进行堆查询,执行一个字符串提供的查询,并创建一个严重的安全问题。
防止SQL注入
可以在脚本语言,如 Perl和PHP巧妙地处理所有转义字符。MySQL扩展为PHP提供mysql_real_escape_string()函数来转义输入的特殊字符。
if (get_magic_quotes_gpc()) { $name = stripslashes($name); } $name = mysql_real_escape_string($name); mysql_query("SELECT * FROM users WHERE name='{$name}'");
LIKE的困境
为了解决LIKE困境,自定义的转义机制必须把用户提供%和_字符到常量。使用addcslashes()函数,它可以让指定的字符转义。
$sub = addcslashes(mysql_real_escape_string("%something_"), "%_"); // $sub == \%something\_ mysql_query("SELECT * FROM messages WHERE subject LIKE '{$sub}%'");