如果用户输入未经修改就插入到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注入攻击不仅仅依赖于能够注入X=Y。

如果攻击者试图通过PHP的$_GET变量或URL的查询字符串侵入表单,如果他们不安全,您将能够抓住他们。

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

因为1=1、2=2、1=2、2=1、1+1=2等……是攻击者SQL数据库的常见问题。也许它也被许多黑客应用程序使用。

但您必须小心,不能从站点重写安全查询。上面的代码为您提供了一个提示,可以重写或重定向(这取决于您)将特定的动态查询字符串黑客入侵到一个页面中,该页面将存储攻击者的IP地址,甚至是他们的COOKIES、历史记录、浏览器或任何其他敏感信息,因此您可以稍后通过禁用他们的帐户或联系当局来处理他们。

其他回答

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

我使用三种不同的方法来防止我的web应用程序容易受到SQL注入的攻击。

使用mysql_real_aescape_string(),这是PHP中的一个预定义函数,此代码为以下字符添加反斜杠:\x00、\n、\r、\、'、“和\x1a。将输入值作为参数传递,以最大限度地减少SQL注入的机会。最先进的方法是使用PDO。

我希望这对你有帮助。

考虑以下查询:

$iId=mysql_real_ascape_string(“1或1=1”);$sSql=“SELECT*FROM table WHERE id=$iId”;

mysql_real_aescape_string()在这里不起保护作用。如果在查询中的变量周围使用单引号(“”),则可以防止这种情况发生。下面是一个解决方案:

$iId=(int)mysql_real_aescape_string(“1或1=1”);$sSql=“SELECT*FROM table WHERE id=$iId”;

这个问题有一些很好的答案。

我建议,使用PDO是最好的选择。

编辑:

自PHP 5.5.0起,mysql_real_aescape_string()已被弃用。使用mysqli或PDO。

mysql_real_aescape_string()的替代方法是

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

例子:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");

要使用参数化查询,需要使用Mysqli或PDO。要用mysqli重写示例,我们需要以下内容。

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli("server", "username", "password", "database_name");

$variable = $_POST["user-input"];
$stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");
// "s" means the database expects a string
$stmt->bind_param("s", $variable);
$stmt->execute();

你想在那里读到的关键函数是mysqli::prepare。

此外,正如其他人所建议的,您可能会发现使用PDO之类的东西来提升抽象层是有用的/更容易的。

请注意,您询问的案例相当简单,更复杂的案例可能需要更复杂的方法。特别地:

如果您希望根据用户输入更改SQL的结构,参数化查询将不会有帮助,并且mysql_real_ascape_string不包含所需的转义。在这种情况下,最好通过白名单传递用户的输入,以确保只允许通过“安全”值。

不推荐的警告:这个答案的示例代码(与问题的示例代码一样)使用了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注入(当转义不够时)

一个简单的方法是使用像CodeIgniter或Laravel这样的PHP框架,它具有内置的过滤和活动记录等功能,因此您不必担心这些细微差别。

对于那些不确定如何使用PDO(来自mysql_函数)的人,我制作了一个非常非常简单的PDO包装器,它是一个单独的文件。它的存在是为了显示应用程序需要做的所有常见事情是多么容易。适用于PostgreSQL、MySQL和SQLite。

基本上,在阅读手册的同时阅读它,了解如何在实际生活中使用PDO函数,从而使以所需格式存储和检索值变得简单。

我想要一列$count=数据库::列('SELECT count(*)FROM `user`');我想要一个数组(key=>value)结果(即,用于生成一个选择框)$pairs=数据库::pairs('SELECT `id',`username`FROM`user`');我想要单行结果$user=DB::行('SELECT*FROM`user`WHERE `id`=?',数组($user_id));我想要一系列结果$banned_users=DB::fetch('SELECT*FROM `user`WHERE `banned`=?',数组('RUE'));