我一直在研究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的安全性?
答案 0 :(得分:10)
你绝对应该使用prepare
API并分别从查询传递值,而不是执行普通字符串插值(例如"SELECT * FROM foo WHERE bar = '$baz'"
→错误强>)。
对于绑定参数,您有三个选项:
使用这些中的哪一个并不重要,它们都同样安全。有关差异的一些详细信息,请参阅这些答案:
使用bindParam
或bindValue
时,传递第三个PDO::PARAM_
参数类型是可选的。如果你没有通过它,它默认将参数绑定为字符串。这意味着您最终可能会得到相当于... WHERE foo = '42'
而不是... WHERE foo = 42
的查询。这取决于您的数据库如何处理它。 MySQL将根据需要自动将字符串转换为数字,就像PHP一样(例如在'42' + 1
中)。其他数据库可能对类型更加挑剔。
同样,所有选项都同样安全。如果您尝试使用'foo'
绑定字符串PDO::PARAM_INT
,则字符串将强制转换为整数,并因此绑定为值0
。没有注射的可能性。
答案 1 :(得分:10)
一个问题有两个。重要的是不要混淆他们
而对于第二个,为了代码的健全和干燥 -
有许多方法可以避免手动绑定。其中一些是:
ORM是简单CRUD操作的绝佳解决方案,必须具备现代应用程序。它将完全隐藏SQL,在幕后进行绑定:
$user = User::model()->findByPk($id);
查询构建器也是一种方法,在某些PHP运算符中伪装SQL但又隐藏了幕后的绑定:
$user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();
某些抽象库可以通过 type-hinted-placeholders 来处理传递的数据,再次隐藏实际的绑定:
$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();
截至第三个问题 - 只要您将数字绑定为字符串(但不是相反!) -
因为mysql总是将您的数据转换为正确的类型。我知道的唯一情况是LIMIT子句,你不能将数字格式化为字符串 - 因此,唯一相关的情况是一个when PDO is set in emulation mode and you have to pass a parameter in LIMIT clause。在所有其他情况下,您可以省略第三个参数,以及显式调用bindValue()
而没有任何问题。
答案 2 :(得分:2)
是的,你应该总是用准备好的陈述来绑定params。 它更安全,并限制SQL注入。但这并不是你必须要做的唯一一个查询params的方法:需要一个正确的类型控件,如果你将一行映射到一个对象中并且如果它有无效数据就会抛出异常,那就更好了。
我希望我能有用!
答案 3 :(得分:1)
是,绑定是要走的路。或参数化查询,这是一个更通用的术语。
@Theo does a wonderful job explaining why parameterized queries is the way to go
您还可以使用stored procedures来获得额外的安全性,但如果您有一个应用程序到一个数据库,则会过度杀死。它适用于一个数据库的多个应用程序,以确保处理数据时的一致性