我们是否应该始终绑定 SQL 语句?

2022-08-31 00:58:33

我一直在研究PDO的.我知道使用PDO准备我的SQL语句可以防止SQL注入发生。bindValue()

代码示例:

$stmt = $dbh->prepare('SELECT * FROM articles WHERE id = :id AND title = :title');
$stmt->bindValue(':id', PDO::PARAM_INT);
$stmt->bindValue(':title', PDO::PARAM_STR);
$stmt->execute();

通过将 ID 绑定为一个数字,并且 Title 是一个字符串,我们可以限制当有人尝试在代码中执行 SQL 注入时造成的损害。

我们是否应该始终使用 a 绑定我们的值,以便限制在 SQL 注入中可以从数据库中提取的内容?在做我们的PDO时,这是否增加了更多的安全性?PDO::PARAM_bindValue()


答案 1

一个问题有两个问题。至关重要的是不要混淆它们

  1. 我们是否应该始终使用占位符来表示查询中的变量数据?
  2. 我们是否应该始终使用应用程序代码中的某些功能来遵循上述规则?
    此外,从开篇帖子下方的评论中的澄清中可以看出第三个问题:
  3. 我们应该始终使用第三个参数,还是可以让PDO默认将所有参数绑定为字符串?

1.对于第一个问题,答案是绝对的 - 是的。

而对于第二个,为了代码的健全性和干燥性 -

2. 尽可能避免人工绑定。

有许多方法可以避免手动绑定。其中一些是:

  • ORM是简单CRUD操作的绝佳解决方案,必须在现代应用程序中使用。它将完全隐藏SQL,在幕后进行绑定:

    $user = User::model()->findByPk($id);
    
  • Query Builder也是要走的路,在某些PHP运算符中伪装SQL,但再次将绑定隐藏在幕后:

    $user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();
    
  • 一些抽象库可以通过类型提示占位符来处理传递的数据,再次隐藏实际的绑定:

    $user = $db->getRow("SELECT * FROM users WHERE id =?i", $id);
    
  • 如果你仍然以上个世纪的方式使用PHP,并且整个代码中都有原始的PDO - 那么你可以在experat()中传递你的变量,仍然节省了大量的输入:

    $stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?');
    $stmt->execute([$id]);
    $user = $stmt->fetch();
    

从第三个问题开始 - 只要你把数字绑定为字符串(但不是相反!) -

3.mysql没问题,几乎可以将每个参数作为字符串发送

因为mysql将始终将数据转换为正确的类型。我所知道的唯一情况是 LIMIT 子句,其中不能将数字格式化为字符串 - 因此,唯一相关的情况是 PDO 设置为仿真模式并且必须在 LIMIT 子句中传递参数的情况。在所有其他情况下,您可以省略第三个参数,以及显式调用,而不会出现任何问题。bindValue()


答案 2

您绝对应该使用API并将值与查询分开传递,而不是进行纯字符串插值(例如 →不好)。prepare"SELECT * FROM foo WHERE bar = '$baz'"

对于绑定参数,您有三个选项:

你使用哪一个并不重要,它们都同样安全。有关差异的一些详细信息,请参阅以下答案:

使用 或 时,传递第三个参数类型是可选的。如果不传递它,则默认将参数绑定为字符串。这意味着您最终可能会得到一个等效于 的查询,而不是 。这取决于您的数据库将如何处理此问题。MySQL将根据需要自动将字符串转换为数字,就像PHP所做的那样(例如在)。其他数据库可能对类型更挑剔。bindParambindValuePDO::PARAM_... WHERE foo = '42'... WHERE foo = 42'42' + 1

同样,所有选项都同样安全。如果您尝试使用 绑定字符串,则该字符串将被转换为整数,并相应地绑定为值 。没有注射的可能性。'foo'PDO::PARAM_INT0


推荐