准备好的语句如何再次保护 SQL 在下面的语句中注入

How prepared statement protect again SQL injection in below statement

我已经阅读了各种文档(SO post)关于 PDO 的 Prepared 语句如何保护用户免受 SQL 注入。

虽然,我理解它保护用户,因为在准备语句中,用户记录直接不在服务器上执行,因为我们发送位置/命名参数(?/:名称),然后我们在执行语句中发送实际数据,并且因此,它使我们免于 SQL 注入。

好吧,现在如果我有下面的 SQL 代码:

$query = "select * from user where id = $user_input_id";

并且用户输入 id = 1

所以查询将类似于:

$query = "select * from user where id = 1"; 

到目前为止,这是完美的。但是如果用户输入 $id = "1; DROP TABLE users;"所以查询将类似于:

$query    = "SELECT * FROM users where id=$id";

因此,它将执行

$query = "SELECT * FROM users where id=1; DROP TABLE users;";

有效,用户 table 将退出,因为此查询直接执行:

好吧,我已经读过准备好的语句可以使用户免于此:

和准备好的语句像:

$data = "1; DROP TABLE users;"

$db->prepare("SELECT * FROM users where id=?");

$db->execute($data);

同样在execute语句中,record with Drop table 是通过的,那么到底怎么不执行drop table statament呢? execute 也在服务器上执行某些部分吗?

任何人都可以解释一下这里准备好的语句如何使用户免于 SQL 注入?

谢谢

这就是为什么您可以额外将绑定数据的类型设置为您需要的类型。

$stm->bindParam(":id", $id, PDO:PARAM_INT)

此外,PDO 会对数据进行一些转义,您提供的字符串不会中断 ; 处的查询,但会作为纯字符串插入数据库中。

如果不显式设置类型(参见 PDOStatement::bindValue() 示例),它会将传递的值视为字符串,因此它会有效地执行此操作:

SELECT * FROM users where id='1; DROP TABLE users;'

顺便说一句,如果您使用模拟的准备好的语句 (PDO::ATTR_EMULATE_PREPARES),这实际上会发生;如果没有这个,它将首先发送参数化查询,然后是实际数据。

SQL 注入是针对 SQL 解析步骤的攻击,而不是语句执行步骤。在这一点上,它与跨站点脚本和 XML 注入攻击等其他解析攻击有相似之处。

SQL 注入之所以有效,是因为通过使用字符串连接运算符将代码和(不受信任的)数据组合在单个字符串中来创建 SQL 语句的常见(损坏)技术允许特制字符串违反语句数据协议(通常通过使用嵌入数据的字符串定界符破坏数据上下文),并允许攻击者操纵 SQL 解析器执行与最初预期不同的代码。

当一个人使用准备好的语句时,就是在告诉解析器 'treat the statement purely as trusted code, and provide some slots into which I will insert the data for execution'。

当你删除字符串 '1;将 table 个用户放入您使用“?”创建的数据槽中占位符,该字符串未被解析器处理,因此它没有机会影响字符串的解析:您使字符串的内容无法脱离数据上下文。

使用您的示例,数据库将执行等效语句:

SELECT * FROM users where id="1; drop table users;"

这是一个完全有效的 select 语句,它可能会或可能不会 return 行,具体取决于 table 中的数据,但几乎可以肯定它不会起作用正确。

然而,该方法绕过了 SQL 注入的尝试。

注意:使用准备好的语句是避免 SQL 注入攻击的唯一通用方法。通常,尝试过滤不受信任的输入数据会失败。