使用参数如何防止 SQL 注入?
How does using parameters prevent SQL injection?
使用参数如何防止SQL注入?
很多人说使用参数而不是输入字符串(例如来自网站用户)可以防止 SQL 注入。
但我不明白,我的意思是 "drop database" 字符串与输入参数的 "drop database" 字符串有何不同,后者在查询中使用?
查看有关 sql 注入攻击的示例:让我们假设这个伪代码:
myfunction(myuser)
{
sql="SELECT user, password FROM users WHERE user='"+myuser+"'"
return db.execute(sql)
}
攻击者会通过此调用来利用此漏洞获取 table:
的所有密码
myfunction("x' OR true OR 'a'='")
相反,如果程序员通过绑定变量执行查询:
myfunction(myuser)
{
sql="SELECT user, password FROM users WHERE user=?"
sql.setParam(1, myUser)
return db.execute(sql)
}
在这种情况下,攻击者输入任何像前一个那样棘手的值都是没有用的,因为数据库引擎会将值100% 透明,而不承认其中的任何控制字符。
SQL 注入通过在 RDBMS 解析 SQL 语法 之前 修改 SQL 查询来工作。
查询参数在之后被发送到服务器 SQL已经被解析,所以恶意值影响[=20的解析已经太晚了=]语法。
查询参数只能解释为标量值,如字符串。
因为仅在字段中输入 "drop database" 本身是不够的。您必须添加一些其他字符才能说服 SQL 解释器终止前一个语句并开始一个新语句(以执行 DROP)。除其他外,参数化将阻止这些类型的序列被 SQL 以预期的方式成功注入和解释。例如 '
等可能关闭字符串的字符将被转义,以便将它们视为变量的一部分,而不是 SQL 的一部分。
具体来说,http://bobby-tables.com/about 包含一个有效的示例。在示例中,有一些 PHP 代码将一行插入 table:
$sql = "INSERT INTO Students (Name) VALUES ('" . $studentName . "');";
execute_sql($sql);
如果 $studentName
设置为 "John" 之类的正常值,则代码生成的最终 SQL 字符串是良性的:
INSERT INTO Students (Name) VALUES ('John');
同样,与您的示例类似,如果将 $studentName
设置为 "DROP TABLE Students",它仍然不会有任何效果。最后的 SQL 将是:
INSERT INTO Students (Name) VALUES ('DROP TABLE Students');
没有造成伤害。
但是...如果 $studentName
设置为更微妙的内容,例如:
Robert'); DROP TABLE Students;--
最终字符串如下所示:
INSERT INTO Students (Name) VALUES ('Robert'); DROP TABLE Students;--');
然后将其传递给 SQL 引擎,引擎将其解释为两个语句(顺便说一句,末尾有一条注释)并执行它们,结果令人不快。
但是,如果 $studentName
中的值作为参数传递而不是仅仅连接到标准字符串,则最终查询将以
结束
INSERT INTO Students (Name) VALUES ('Robert\'); DROP TABLE Students;--');
请注意中间的转义 '
,因此它现在被视为字符串的一部分。它不再导致字符串在 "t" 之后停止。
因此 table 中 Name
字段中输入的内容将是
Robert'); DROP TABLE Students;--
DROP TABLE 语句不会被执行,因为 SQL 解释器永远不会看到它 - 它只是将其视为要添加到 table 的值的一部分。
参数化意味着参数值中的任何内容仅被视为字符串(或可能是数字)——它永远不会逃脱到字符串之外并被视为 SQL 语句本身的一部分。
进一步阅读:
How does the SQL injection from the "Bobby Tables" XKCD comic work?
可能会帮助您更好地理解。
使用参数如何防止SQL注入?
很多人说使用参数而不是输入字符串(例如来自网站用户)可以防止 SQL 注入。
但我不明白,我的意思是 "drop database" 字符串与输入参数的 "drop database" 字符串有何不同,后者在查询中使用?
查看有关 sql 注入攻击的示例:让我们假设这个伪代码:
myfunction(myuser)
{
sql="SELECT user, password FROM users WHERE user='"+myuser+"'"
return db.execute(sql)
}
攻击者会通过此调用来利用此漏洞获取 table:
的所有密码myfunction("x' OR true OR 'a'='")
相反,如果程序员通过绑定变量执行查询:
myfunction(myuser)
{
sql="SELECT user, password FROM users WHERE user=?"
sql.setParam(1, myUser)
return db.execute(sql)
}
在这种情况下,攻击者输入任何像前一个那样棘手的值都是没有用的,因为数据库引擎会将值100% 透明,而不承认其中的任何控制字符。
SQL 注入通过在 RDBMS 解析 SQL 语法 之前 修改 SQL 查询来工作。
查询参数在之后被发送到服务器 SQL已经被解析,所以恶意值影响[=20的解析已经太晚了=]语法。
查询参数只能解释为标量值,如字符串。
因为仅在字段中输入 "drop database" 本身是不够的。您必须添加一些其他字符才能说服 SQL 解释器终止前一个语句并开始一个新语句(以执行 DROP)。除其他外,参数化将阻止这些类型的序列被 SQL 以预期的方式成功注入和解释。例如 '
等可能关闭字符串的字符将被转义,以便将它们视为变量的一部分,而不是 SQL 的一部分。
具体来说,http://bobby-tables.com/about 包含一个有效的示例。在示例中,有一些 PHP 代码将一行插入 table:
$sql = "INSERT INTO Students (Name) VALUES ('" . $studentName . "');";
execute_sql($sql);
如果 $studentName
设置为 "John" 之类的正常值,则代码生成的最终 SQL 字符串是良性的:
INSERT INTO Students (Name) VALUES ('John');
同样,与您的示例类似,如果将 $studentName
设置为 "DROP TABLE Students",它仍然不会有任何效果。最后的 SQL 将是:
INSERT INTO Students (Name) VALUES ('DROP TABLE Students');
没有造成伤害。
但是...如果 $studentName
设置为更微妙的内容,例如:
Robert'); DROP TABLE Students;--
最终字符串如下所示:
INSERT INTO Students (Name) VALUES ('Robert'); DROP TABLE Students;--');
然后将其传递给 SQL 引擎,引擎将其解释为两个语句(顺便说一句,末尾有一条注释)并执行它们,结果令人不快。
但是,如果 $studentName
中的值作为参数传递而不是仅仅连接到标准字符串,则最终查询将以
INSERT INTO Students (Name) VALUES ('Robert\'); DROP TABLE Students;--');
请注意中间的转义 '
,因此它现在被视为字符串的一部分。它不再导致字符串在 "t" 之后停止。
因此 table 中 Name
字段中输入的内容将是
Robert'); DROP TABLE Students;--
DROP TABLE 语句不会被执行,因为 SQL 解释器永远不会看到它 - 它只是将其视为要添加到 table 的值的一部分。
参数化意味着参数值中的任何内容仅被视为字符串(或可能是数字)——它永远不会逃脱到字符串之外并被视为 SQL 语句本身的一部分。
进一步阅读:
How does the SQL injection from the "Bobby Tables" XKCD comic work?
可能会帮助您更好地理解。