带有绑定参数的预准备语句优于带有转义/引用参数的插值语句的原因

Bor*_*pen 4 sql sql-injection

为了防止 SQL 注入,建议使用带有绑定值的准备好的语句。这确保了数据库可以区分 SQL 中的实际逻辑(必须被解析、解释和优化)和数据(不需要解释),因此不会解释和执行在数据中找到的命令.

另一种实现某种保护的方法是使用转义库,它可以解除数据中的重要字符,这样它们就不会被解释。

在我看来,通常建议使用带有绑定参数的准备好的语句而不是转义输入。例如,带有绑定值的准备好的语句确实在循环中具有一些性能优势。

我的问题:是否有任何安全原因更喜欢带有绑定值的准备语句而不是转义?如果是,确切原因是什么?

我可能会想到的一个原因是“转义很棘手”并且转义库需要与数据库功能完全匹配……还有什么?

Bil*_*win 6

原因之一是转义仅适用于保护带引号的字符串文字。例如(我将使用伪代码,因为您没有引用任何特定的编程语言):

$escapedName = EscapeString("O'Reilly")

$sql = "SELECT * FROM MyTable WHERE name = '$escapedName'"
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,撇号应该被转义,所以它会变得WHERE name = 'O\'Reilly'安全,因此可以安全地插入到 SQL 查询中而不会导致任何错误。

但是,在 SQL 中不需要引用数字,并且转义包含撇号的字符串不会做正确的事情:

$escapedId = EscapeString("123'456")

$sql = "SELECT * FROM MyTable WHERE id = $escapedId"
Run Code Online (Sandbox Code Playgroud)

这将导致WHERE id = 123\'456which 仍然是一个错误。

您可能会说,“把数字放在单引号中”,但这并不总是可行的,例如LIMITMySQL 中的子句需要实整数,而不是包含数字的带引号的字符串。

除了上述问题之外,使用参数而不是使用转义来编写代码更容易

例如,您可以编写如下代码:

$sql = "INSERT INTO mytable (col1, col2, col3, col4, col5, col6) 
  VALUES ('" . mysqli_real_escape_string($_POST['col1']) . "', " 
  . $mysqli->real_escape_string($_POST['col2']) . "', '" 
  . $mysqli->real_escape_string($_POST['col3']) . "', '" 
  . $mysqli->real_escape_string($_POST['col4']) . ", '" 
  . $mysqli->real_escape_string($_POST['col5']) . "', '" 
  . $mysqli->real_escape_string($_POST['col6']) . "')";
Run Code Online (Sandbox Code Playgroud)

你能发现错误吗?有足够的时间,我相信你可以。但它会减慢您的编码速度,并可能让您在寻找丢失的引号字符和其他错误时眼睛疲劳。

但是写这个要容易得多,之后也更容易阅读:

$sql = "INSERT INTO mytable (col1, col2, col3, col4, col5, col6) 
  VALUES (?, ?, ?, ?, ?, ?)";
Run Code Online (Sandbox Code Playgroud)

查询参数对于更多数据类型是安全的,它们可以帮助您更快地编写代码,减少错误。这是一个很大的胜利。