我一直在研究PDO的bindValue()
。我知道用PDO准备SQL语句可以防止SQL注入的发生。
代码例子:
$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注入时所造成的损害。
我们是否应该总是将我们的值与PDO::PARAM_
绑定,以便我们可以限制在SQL注入中可以从数据库提取的内容?在做bindValue()
时,这是否增加了PDO的安全性?
一个问题包含两个问题。重要的是不要混淆它们
- 我们应该总是使用占位符来表示查询中的变量数据吗?我们是否应该总是在应用程序代码中使用某些函数来遵循上述规则?
- 我们应该总是使用第三个参数,还是让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,那么你可以在execute()中传递变量,仍然节省了大量的输入:
$stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?'); $stmt->execute([$id]); $user = $stmt->fetch();
关于第三个问题——只要你将数字绑定为字符串(而不是相反!)——
3。对于mysql来说,将几乎所有参数作为字符串
发送是没问题的。作为mysql将始终转换您的数据到适当的类型。我所知道的唯一情况是,在LIMIT子句中,您不能将数字格式化为字符串-因此,唯一相关的情况是当PDO设置为仿真模式时,您必须在LIMIT子句中传递参数。在所有其他情况下,您可以省略第三个参数,以及显式调用bindValue()
,没有任何问题。
你绝对应该使用prepare
API并从查询中单独传递值,而不是做纯字符串插值(例如"SELECT * FROM foo WHERE bar = '$baz'"
→bad)。
对于绑定参数,您有三个选项:
-
bindParam
-
bindValue
-
execute
你使用哪一种并不重要,它们都同样安全。有关差异的一些详细信息,请参阅以下回答:
- bindValue()和bindParam()之间的混淆?
- 使用PDO而不绑定
当使用bindParam
或bindValue
时,传递第三个PDO::PARAM_
参数类型是可选的。如果不传递它,它默认将参数绑定为字符串。这意味着您最终得到的查询可能相当于... WHERE foo = '42'
而不是... WHERE foo = 42
。这取决于你的数据库如何处理这个问题。MySQL会根据需要自动将字符串转换为数字,就像PHP一样(例如在'42' + 1
中)。其他数据库可能对类型更挑剔。
同样,所有选项都同样安全。如果您试图使用PDO::PARAM_INT
绑定字符串'foo'
,则该字符串将被强制转换为整数,并相应地绑定为值0
。
是的,您应该始终将参数与预处理语句绑定。它更安全,并且限制了SQL注入。但这并不是查询参数必须做的唯一事情:需要正确的类型控制,最好是将一行映射到对象中,并在其中包含无效数据时抛出异常。
希望我能帮上忙!
是的,绑定是可行的方法。或参数化查询,更广义的术语。
@Theo很好地解释了为什么参数化查询是一种方式
您也可以使用存储过程来获得额外的安全性,但是如果您有一个应用程序到一个数据库,那么这是多余的。在处理数据