如果用户输入未经修改就插入到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;--')

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


当前回答

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

使用PDO和MYSQLi是防止SQL注入的好方法,但如果您真的想使用MySQL函数和查询,最好使用

mysql_real_reape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

还有更多的功能可以防止这种情况:比如识别-如果输入是字符串、数字、字符或数组,那么有很多内置函数可以检测这种情况。此外,最好使用这些函数来检查输入数据。

is_string(is_string)

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

是数字(_N)

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

使用这些函数来检查mysql_real_aescape_string中的输入数据要好得多。

其他回答

警告:本答案中描述的方法仅适用于非常特定的场景,并不安全,因为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、历史记录、浏览器或任何其他敏感信息,因此您可以稍后通过禁用他们的帐户或联系当局来处理他们。

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

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

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

安全警告:此答案不符合安全最佳实践。转义不足以防止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");

这个问题的简单替代方案可以通过在数据库本身中授予适当的权限来解决。例如:如果您使用的是MySQL数据库,则通过终端或提供的UI输入数据库,然后执行以下命令:

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

这将限制用户只能受限于指定的查询。删除删除权限,这样数据就永远不会从PHP页面发出的查询中删除。第二件事是刷新特权,以便MySQL刷新权限和更新。

FLUSH PRIVILEGES; 

有关刷新的详细信息。

要查看用户的当前权限,请启动以下查询。

select * from mysql.user where User='username';

了解更多有关GRANT的信息。

我几年前就写过这个小函数:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

这允许在一行C#字符串中运行语句。格式如下:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

考虑到变量类型,它会逃逸。若您试图参数化表、列名称,它将失败,因为它将每个字符串放在引号中,这是无效语法。

安全更新:以前的str_replace版本允许通过向用户数据中添加{#}标记进行注入。如果替换包含这些令牌,则preg_replace_callback版本不会导致问题。