Php PDO参数绑定的安全性

Php PDO参数绑定的安全性,php,security,pdo,Php,Security,Pdo,我使用参数化查询和PDO从一个MySQL数据库中检索数据,并使用一个相当复杂的查询。作为这项工作的一部分,我使用了一些类似的代码: 查询: $query = "SELECT * FROM table WHERE something = :id_$id"; $stmt = $db->prepare($query); 参数绑定: $stmt->bindParam(":id_$id", $id); 我的理解是PDO通过“清理”bindParam调用中的替换字符串来清理参数化输入,但我的

我使用参数化查询和PDO从一个MySQL数据库中检索数据,并使用一个相当复杂的查询。作为这项工作的一部分,我使用了一些类似的代码:

查询:

$query = "SELECT * FROM table WHERE something = :id_$id";
$stmt = $db->prepare($query);
参数绑定:

$stmt->bindParam(":id_$id", $id);
我的理解是PDO通过“清理”bindParam调用中的替换字符串来清理参数化输入,但我的问题是: 攻击者是否可以通过$id值利用上述构造来注入不需要的SQL

PDO对:id_$id进行了文本替换,替换值为$id,因此我认为:id_$id的任何部分,不管它最终是什么,都不应该出现在最终查询中,但我希望得到一个明确的答案

编辑:在解释为什么我认为这是一件安全的事情时,我似乎没有像我应该解释的那样清楚。当然,我并不认为这是一种好的做事方式

我认为这可能是安全的原因是,如果这是错误的,PDO会纠正我,在替换文本上对经过消毒的绑定参数进行文本替换。直观地说,这应该表明替换文本:id_$id可以是任何值,因为在查询中放置参数时,它将完全被PDO替换。由于参数替换涉及清理参数的值,而:id_$id执行起来可能很危险,$id是最终查询中显示的内容,应该是安全的


不管怎样,这就是我的理由。在我的代码中,我没有做任何危险的事情,因此这更像是一种学术兴趣。

pdo确实清理了输入,您可以在bindValue/bindParam的第三个参数中进一步指定类型。我会避免使用$作为令牌的一部分,因为php可能会在以下情况下解释它:

$query = "SELECT * FROM table WHERE something = :id;";
$stmt = $db->prepare($query);
$stmt->bindParam(":id", $id, PDO::PARAM_INT);
这将确保如果$id不是int,pdo将引发异常


即使尝试以这种方式注入sql也会被转义。

当然,它是易受攻击的

但是,使用命名占位符是完全可选的。因此,您根本不必使用它们:

$query = "SELECT * FROM table WHERE something = ?";
$stmt = $db->prepare($query);
$stmt->execute(array($id));

和,你知道,任何相当复杂的代码都可以被简化。

你应该考虑使用一组封闭的允许值作为ID。我是说:

switch ($id) {
  case "value01":
    $param = ":id_01";
    break;

  case "value02":
    $param = ":id_02";
    break;

  default:
    // safe value
    $param = ":id_00";
}

$query = "SELECT * FROM table WHERE something = $param";
$stmt = $db->prepare($query);
$stmt->bindParam("$param", $id);

考虑一下$id='或1=1',看看它准备了什么。问题的棘手部分是,替换参数将变成::id_uu或1=1,我认为应该在最后的文本中用消毒版本的或1=1替换它,它不会执行。我可以自己运行更多的测试,但是我更愿意从知道答案的人那里得到专家的意见,而不仅仅是假设我已经测试了所有的输入。啊,我明白了这一点,bindParam在注入的情况下永远不会绑定一个正确的param,导致“参数未定义”的执行错误。我个人确实认为此时没有可行的利用,但专家的意见是:未经利用的变量在准备好的字符串中没有位置,即使您当时无法想到利用,当然也没有理由。请记住,仅绑定一个参数是没有字符串替换的::id_uu或1=1不会被替换,但需要您绑定一个:id_uuuu参数才能成功。您能解释最后一部分吗?我想得越多,我就越确信,考虑到您第一条评论中的“错误”输入,您应该会看到以下::id_$id-php string replace->:id_uu或1=1,然后通过PDO将其替换为:id_$id-php string replace->:id_u或1=1-magic PDO escaping->。并非每个字符在命名参数中都有效。参数可通过两种方式给出:?,或OTOH,:,后跟[a-zA-Z_],后跟一个或或moe[a-zA-Z0-9_]。然而,这在某种程度上依赖于数据库,MySQL甚至不支持命名参数,所以PDO会模拟它们。我必须深入研究C代码才能确定,但是“或”空格绝对无效。在查询字符串中替换$id是有意的,这正是我要问的。明白了,但还是应该避免。你不觉得这实际上破坏了准备好的陈述的想法吗?我从来没有这样做过,我不建议这样做。这只是为了给用户找到一个变通方法,让他们能够做自己想做的事情。虽然你的例子并不是我想要完成的,但它与我的实际实现有些相似。我没有在:id_$id中为我的$id使用来自用户的值,而是使用了一个我知道是安全的并行映射。你能证明它是易受攻击的吗?我将用一个更具体的例子来更新我的问题,说明为什么我相信它可能是安全的。我找到了一个绝对安全的方法来解决我的问题,但我仍然希望有一个答案来满足我对这个问题的学术兴趣:你注意到问题下的第一条评论了吗?是的,这是正确的答案。只要使用准备好的语句,除非有很好的理由,否则不要使用变量