Php 使用MySQLI的SQL注入攻击的简短解决方案

Php 使用MySQLI的SQL注入攻击的简短解决方案,php,mysql,security,mysqli,Php,Mysql,Security,Mysqli,我养成了编写以下代码的习惯: $q = mysqli_query($mysqli,"SELECT * FROM table WHERE a='$a', b=$b;"); while ($row = mysqli_fetch_array($q)) { // do something } 其中,$a是用户输入的字符串(通过$\u GET获取),而$b是用户输入的整数 显然,我上面的代码容易受到SQL注入攻击,所以我的习惯是这样重写它: $q = mysqli_query($mysq

我养成了编写以下代码的习惯:

$q = mysqli_query($mysqli,"SELECT * FROM table WHERE a='$a', b=$b;");
while ($row = mysqli_fetch_array($q)) {
    // do something
    }
其中,
$a
是用户输入的字符串(通过
$\u GET
获取),而
$b
是用户输入的整数

显然,我上面的代码容易受到SQL注入攻击,所以我的习惯是这样重写它:

$q = mysqli_query($mysqli,"SELECT * FROM table WHERE a='".str_replace("'","",$a)."', b=".($b+0).";");
function refValues($arr){
    if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
    {
        $refs = array();
        foreach($arr as $key => $value)
            $refs[$key] = &$arr[$key];
        return $refs;
    }
    return $arr;
}
function safequery($a,$b,$c) {
    global $mysqli;
    $q = mysqli_prepare($mysqli,$a);
    call_user_func_array("mysqli_stmt_bind_param",refValues(array_merge(array($q,$b),$c)));
    mysqli_stmt_execute($q);
    return $q;
}

safequery("SELECT * FROM table WHERE a=? AND b=?;","si",array("unsafestring",37));
但是如果
$a
需要使用撇号(或者在使用引号标记字符串时使用引号),那么这当然会有问题

最近,我在
mysqli
中了解了一些准备好的语句,并开始使用它们。我编写了以下函数,以便在无需更改代码的情况下更轻松地进行调用:

function safequery($a,$b,$c) {
    global $mysqli;
    $q = mysqli_prepare($mysqli,$a);
    $e = "mysqli_stmt_bind_param(\$q,\$b";
    $i = 0;
    while ($i < count($c)) {
        $e.=",";
        $e.="\$c[$i]";
        $i++;
        }
    $e.=");";
    eval($e);
    mysqli_stmt_execute($q);
    return $q;
    }

safequery("SELECT * FROM table WHERE a=? AND b=?;","si",array("unsafestring",37));
函数safequery($a、$b、$c){
全球$mysqli;
$q=mysqli\u prepare($mysqli,$a);
$e=“mysqli\u stmt\u bind\u参数(\$q,\$b”;
$i=0;
而($i
但是从这个函数返回的结果不是
mysqli\u结果
,因此不适用于上面的第一位代码。经过更多的研究,我找到了一个替代方案,但这需要对我如何编写代码进行彻底的反思。这是必要的还是可能的,以防止MySQL注入攻击,只需对第一位代码进行少量更改(没有新行、相同的输出样式等)


我环顾了StackOverflow和web的其他部分,但找不到一个好的简单解决方案;所有这些都要求每次调用至少多编辑三行,并且每行的读取方式都不同。我更愿意做这个程序性的工作。

不要认为半途而废就能解决这个问题。承诺从代码中删除所有插值错误,并严格遵守使用准备好的语句的规则。你提出的解决方案只会让事情变得更糟,它给你一种虚假的安全感。这也比使用预先准备好的语句要多得多,所以我不知道你为什么要这样做

一种更容易做到这一点的方法是从使用双引号切换到使用双引号
到查询中的单引号
以禁用插值。任何转义错误都会成为语法问题,如果编辑器突出显示这些错误,您将能够在整个房间中发现它们,如果侥幸成功,您将插入无害的内容,例如
$a
,而不是实际的数据

另一个需要考虑的问题是,你应该使用ORM,或者给出你所知道的应用程序的复杂程度。从实现的角度来看,这些可以使事情变得相当容易

你的密码就像一颗滴答作响的定时炸弹,尽快把它扔掉。不要认为替换引号就足够了,这只解决了一个问题,实际上还有许多其他方法,您的应用程序可能容易受到注入错误的攻击。像这样的工具有一整套工具,它们可以尝试破坏你的代码,如果你看一看它可以做的事情列表,如果它发现一个缺陷,你可能会想马上修复这些问题

找到问题的一种方法是使用grep这样的工具:

grep query `find -name '*.php'` | grep '\$'
这不是防弹的,但可能会出现很多代码,您应该立即修复


正如@ceejayoz所建议的,从您的计算机中清除带有
eval
的函数,永远不要再这样做。

不要认为半途而废就能解决这个问题。承诺从代码中删除所有插值错误,并严格遵守使用准备好的语句的规则。你提出的解决方案只会让事情变得更糟,它给你一种虚假的安全感。这也比使用预先准备好的语句要多得多,所以我不知道你为什么要这样做

一种更容易做到这一点的方法是从使用双引号切换到使用双引号
到查询中的单引号
以禁用插值。任何转义错误都会成为语法问题,如果编辑器突出显示这些错误,您将能够在整个房间中发现它们,如果侥幸成功,您将插入无害的内容,例如
$a
,而不是实际的数据

另一个需要考虑的问题是,你应该使用ORM,或者给出你所知道的应用程序的复杂程度。从实现的角度来看,这些可以使事情变得相当容易

你的密码就像一颗滴答作响的定时炸弹,尽快把它扔掉。不要认为替换引号就足够了,这只解决了一个问题,实际上还有许多其他方法,您的应用程序可能容易受到注入错误的攻击。像这样的工具有一整套工具,它们可以尝试破坏你的代码,如果你看一看它可以做的事情列表,如果它发现一个缺陷,你可能会想马上修复这些问题

找到问题的一种方法是使用grep这样的工具:

grep query `find -name '*.php'` | grep '\$'
这不是防弹的,但可能会出现很多代码,您应该立即修复


正如@ceejayoz所建议的,从您的计算机中清除带有
eval
的函数,并且永远不要再这样做。

在与评论者交谈并查看他们链接和建议的一些其他问题和事情后,我已经重写了第三段代码,既解决了问题,又修复了一些评论者指出的安全漏洞(如果还有其他漏洞,请告诉我)

首先介绍我使用的
eval()
。虽然我看不出它会立即导致问题(用户字符串在
eval()
中不是作为代码执行的),但这确实是一种迂回而愚蠢的解决问题的方法@塔德曼建议吃鳕鱼
return mysqli_stmt_get_result($q);