更安全的PDO :: prepare?

时间:2016-09-24 18:21:59

标签: php mysql pdo sql-injection

使用PDO::prepare可能会大大降低SQL注入的可能性,因为它允许参数化查询。但是,由于它还允许非参数化查询,其安全性完全取决于它的使用方式。

对于我相当缺乏经验的编程团队,我考虑在PDO::prepare上放置一个包含额外级别的过滤器的包装器:如果参数包含任何单引号或双引号,则会生成错误。

现在,在代码检查期间,我们所要做的就是检查每个查询是否使用包装器而不是PDO::prepare(或任何其他执行查询的函数)。当我们发现违规时,它需要被修复,或者,在极少数情况下,它涉及一个不能由参数处理的动态查询,例如变量列或表名。 (在我们构建的应用程序类型中非常不寻常。)然后可以非常仔细地研究这些少数情况,以确保它们安全。

我的问题是:在声明中排除引号是否会阻止SQL注入非参数化查询,正如我所假设的那样,并且它是一种使代码检查更可靠和有效的实用工具吗? / p>

2 个答案:

答案 0 :(得分:4)

不,这不是一个实际的解决方案。在SQL语句中使用引号是有正当理由的,因为字符串文字和日期文字使用引号。没有正当理由在SQL查询中对每个字符串或日期常量使用查询参数。它只会使您的查询不可读,并且在某些情况下会产生性能成本。

SQL注入也有机会与引号无关。如果在没有良好实践的情况下实施,那么形成SQL查询的动态内容(即应用程序变量)的任何部分都有可能变得不安全。

SQL中的数字常量不需要引号。您的过滤功能无法捕获这些情况。

$sql = "SELECT * FROM MyTable WHERE id = $UnsafeVariable";

我与PHP安全专家和作者交谈过,他声称没有查询参数不是解决方案的情况。我向他展示了这个问题:

$sql = "SELECT * FROM MyTable ORDER BY $SomeColumn $AscOrDesc";

当您拥有允许用户选择要对其进行排序的列的用户界面,或单击向上箭头或向下箭头时,这是您在Web应用程序中要执行的操作的一个非常明确的示例选择排序方向。这些是不涉及引号的情况,不能使用预准备语句进行参数化,但如果$SomeColumn$AscOrDesc包含未经验证的内容,它们仍然存在SQL注入漏洞的风险。

制定像你描述的那样的一揽子规则也不实际。总有例外情况。您将花费太多时间查看这些案例并授权开发人员绕过您的过滤功能。 “一刀切”规则的问题在于它们没有。

代码审核是提高代码质量的经济实用的方法。

更好的解决方案是允许开发人员以最实用的方式编写代码,但创建一个每个代码提交通过代码审查的策略。

我们在现有公司这样做。即使是高级开发人员也需要让其他开发人员与他们进行代码审查。我们使用github,我们已经开始练习每个重要的代码更改遵循以下步骤:

  1. 从主开发分支创建分支。
  2. 在该分支中开发修复程序,根据需要多次提交。
  3. 提交者发出拉取请求。这会将所有提交合并为一个易于查看的差异。
  4. 对等体对拉取请求执行代码审查。他们可以使用github对pull请求发表评论,以提出问题或建议更正。这会创建代码审查发生的日志。
  5. 提交者根据代码审查进行修复。
  6. 代码审核者进行最终审核,然后批准拉取请求,将修复程序合并到主分支中。可以删除临时分支。
  7. 这不仅会抓住错误(即使是高级开发人员也会错过一些东西),但向初级开发人员展示更多代码只会提高他们的技能。随着初级开发人员的成长,这会产生乘数效应,因为他们减少了新手错误,他们甚至可以成为高级开发人员来帮助下一组青少年。

答案 1 :(得分:1)

  

在声明中排除引号会阻止SQL注入吗?

NO

最近我写了一篇文章,除了你应该知道的其他内容之外,它还包含一个 doesn't involve a single quote 的SQL注入工作示例:

<form method=POST>
<input type=hidden name="name=(SELECT password from admins)WHERE`id`=1#" value="">
<input type=hidden name="name" value="Joe">
<input type=hidden name="id" value="1">
<input type=submit>
</form>
<?php
if ($_POST) {
    $pdo = new PDO('mysql:dbname=test;host=localhost', 'root', '');
    $params = [];
    $setStr = "";
    foreach ($_POST as $key => $value)
    {
        if ($key != "id")
        {
            $setStr .= addslashes($key)." = :".$key.",";
        }
        $params[$key] = $value;

    }
    $setStr = rtrim($setStr, ",");
    $pdo->prepare("UPDATE users SET $setStr WHERE id = :id")->execute($params);
    var_dump("UPDATE users SET $setStr WHERE id = :id", $_POST);
}   
  

这是一种使代码检查更可靠,更有效的实用工具吗?

没有丝毫。

  1. 想象一下,在这个网站上使用了一个。您只是无法发布您的问题!这证明了这种方法不可靠。
  2. 如上所示,注射不需要引号
  3. 创建一个100%万无一失的工具是不可能的。你必须投资你的团队,而不是笨拙的工具。