如果用户输入未经修改就插入到SQL查询中,则应用程序很容易受到SQL注入的攻击,如下例所示:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

这是因为用户可以输入类似值的内容);DROP TABLE表;--,并且查询变为:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

可以采取什么措施防止这种情况发生?


当前回答

安全警告:此答案不符合安全最佳实践。转义不足以防止SQL注入,请改用准备好的语句。使用以下概述的策略,风险自负。(此外,在PHP7中删除了mysql_real_ascape_string()。)

警告:mysql扩展名此时被删除。我们建议使用PDO扩展

使用此PHP函数mysql_escape_string(),您可以快速获得良好的预防效果。

例如:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string-转义用于mysql_query的字符串

为了更好地预防,您可以在末尾添加。。。

wHERE 1=1   or  LIMIT 1

最后你得到:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1

其他回答

一个好主意是使用像Idiorm这样的对象关系映射器:

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

它不仅可以避免SQL注入,还可以避免语法错误!它还支持具有方法链接的模型集合,以一次过滤或将操作应用于多个结果和多个连接。

不推荐的警告:这个答案的示例代码(与问题的示例代码一样)使用了PHP的MySQL扩展,该扩展在PHP 5.5.0中被弃用,在PHP 7.0.0中被完全删除。安全警告:此答案不符合安全最佳实践。转义不足以防止SQL注入,请改用准备好的语句。使用以下概述的策略,风险自负。(此外,在PHP7中删除了mysql_real_ascape_string()。)重要的防止SQL注入的最佳方法是使用Prepared语句而不是转义,正如公认的答案所示。有Aura.Sql和EasyDB这样的库可以让开发人员更容易地使用准备好的语句。若要了解更多有关为什么准备好的语句更善于停止SQL注入的信息,请参阅此mysql_real_ascape_string()旁路以及最近修复的WordPress中的Unicode SQL注入漏洞。

注入预防-mysql_real_ascape_string()

PHP有一个专门制作的函数来防止这些攻击。您所需要做的就是使用一个函数,mysql_real_aescape_string。

mysql_real_aescape_string获取一个将在mysql查询中使用的字符串,并返回同一个字符串,所有SQL注入尝试都安全逃脱。基本上,它会将用户可能输入的那些麻烦的引号(')替换为MySQL安全的替代品,即转义引号“”。

注意:您必须连接到数据库才能使用此功能!

//连接到MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

您可以在MySQL-SQL注入预防中找到更多详细信息。

不推荐的警告:这个答案的示例代码(与问题的示例代码一样)使用了PHP的MySQL扩展,该扩展在PHP 5.5.0中被弃用,在PHP 7.0.0中被完全删除。安全警告:此答案不符合安全最佳实践。转义不足以防止SQL注入,请改用准备好的语句。使用以下概述的策略,风险自负。(此外,在PHP7中删除了mysql_real_ascape_string()。)

参数化查询AND输入验证是一种方法。尽管使用了mysql_real_ascape_string(),但在许多情况下可能会发生SQL注入。

这些示例易受SQL注入攻击:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

or

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

在这两种情况下,都不能使用“”来保护封装。

来源:意外的SQL注入(当转义不够时)

我建议使用PDO(PHP数据对象)运行参数化SQL查询。

这不仅可以防止SQL注入,还可以加快查询速度。

而且,通过使用PDO而不是mysql_、mysqli_和pgsql_函数,可以使应用程序从数据库中抽象出一点,这是很少需要切换数据库提供程序的情况。

安全警告:此答案不符合安全最佳实践。转义不足以防止SQL注入,请改用准备好的语句。使用以下概述的策略,风险自负。

你可以做一些基本的事情,比如:

$safe_variable = mysqli_real_escape_string($dbConnection, $_POST["user-input"]);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

这并不能解决所有问题,但它是一个很好的垫脚石。我省略了一些明显的项目,例如检查变量的存在性、格式(数字、字母等)。