在 PHP 中使用准备好的语句/存储过程时如何保护自己免受 SQL 注入?

Posted

技术标签:

【中文标题】在 PHP 中使用准备好的语句/存储过程时如何保护自己免受 SQL 注入?【英文标题】:How do protect yourself against SQL injection when using prepared statements/store procedures in PHP? 【发布时间】:2011-09-25 20:52:07 【问题描述】:

自从阅读Is mysql_real_escape_string enough to Anti SQL Injection? 后,我一直在研究如何最好地防止 php/mysql 中的 sql 注入,而不仅仅是使用 mysqli/mysql 真正的转义。

我看过这个很好的帖子How can I prevent SQL injection in PHP?

我曾经在桌面/内部工具上做很多 ms sql server 的工作,我们总是编写存储过程来防止这种情况发生,所以我使用 PDO http://php.net/manual/en/pdo.prepared-statements.php 阅读了 PHP/mysql 中的等价物

上面有一行:

prepared statements 的参数不需要引用;驱动程序会自动处理这个。如果应用程序专门使用预准备语句,开发人员可以确定不会发生 SQL 注入(但是,如果查询的其他部分是使用非转义输入构建的,则 SQL 注入仍然是可能的)。

我一直相信 PDO 确实可以防止 sql 注入攻击,所以任何人都可以提供一个从安全角度来看 PDO 不够用的实例吗?

【问题讨论】:

【参考方案1】:

决定性的不是您使用的结构(存储过程、准备好的语句等),而是您是否在任何时候使用未经检查的用户输入将 SQL 连接在一起。例如,您可以在存储过程中执行动态 SQL,在这种情况下危险仍然存在。

最简单的方法(从避免注入的角度来看)是使用带有绑定变量的 SP 或 PS:这些不需要检查,因为它们将被识别为预定义占位符内的值。

【讨论】:

【参考方案2】:

您仍然可以从存储过程中获取 SQL 注入,这些存储过程在内部使用 PREPARE 语法(在 MySQL 中)来创建动态 SQL 语句。

这些需要非常小心地完成,必要时使用 QUOTE()。

理想情况下,我们不需要在存储例程中使用 PREPARE,但在某些情况下,它会变得非常难以避免:

在 MySQL 5.5 之前,LIMIT 子句不能使用非常量值。 IN() 子句中使用的列表不能(明智地)参数化,因此如果使用此模式,则需要使用动态 SQL 有时需要使用动态生成的 ORDER BY 子句。

在需要使用 PREPARE 的情况下,我会按优先顺序推荐:

如果某物是 INT 类型(等),则它不易受到 SQL 注入的影响,您可以毫无问题地将值放入查询中(例如,对于 LIMIT) 字符串值可以放在 EXECUTE 之前的 @variable 中,或者传递到 EXECUTE 子句中 需要检查列表值(例如 IN())的有效性。 最后,QUOTE() 可用于引用字符串值,这在某些情况下很有用

【讨论】:

以上是关于在 PHP 中使用准备好的语句/存储过程时如何保护自己免受 SQL 注入?的主要内容,如果未能解决你的问题,请参考以下文章

PHP:使用准备好的语句进行注入保护

创建 MySQL 准备好的语句

2个准备好的语句,2个存储过程,1个mysqli连接

如何摆脱 MySQL 错误“需要重新准备准备好的语句”

PHP:使用准备好的语句并防止 SQL 注入与逃逸

如何查看准备好的语句的内容?