准备好的语句中的参数化表名:一个困境
尽管 mysqli_stmt_bind_param 已被证明可以有效防止 SQL 注入,但当变量影响时,它会遇到限制涉及到表名。如提供的代码片段所示:
function insertRow( $db, $mysqli, $new_table, $Partner, $Merchant, $ips, $score, $category, $overall, $protocol ) { $statement = $mysqli->prepare("INSERT INTO " .$new_table . " VALUES (?,?,?,?,?,?,?);"); mysqli_stmt_bind_param( $statment, 'sssisss', $Partner, $Merchant, $ips, $score, $category, $overall, $protocol ); $statement->execute(); }
$new_table 的有问题的串联引入了 SQL 注入漏洞。尝试用另一个占位符替换它,如以下代码片段所示,失败:
function insertRow( $db, $mysqli, $new_table, $Partner, $Merchant, $ips, $score, $category, $overall, $protocol ) { $statement = $mysqli->prepare("INSERT INTO (?) VALUES (?,?,?,?,?,?,?);"); mysqli_stmt_bind_param( $statment, 'ssssisss', $new_table, $Partner, $Merchant, $ips, $score, $category, $overall, $protocol ); $statement->execute(); }
准备语句的局限性
核心问题在于无法准备好的语句来保护定义 SQL 语句结构的参数,例如表名。这是因为准备好的语句只允许参数值不改变语句的含义。由于表名决定了 SQL 语句的有效性,因此在执行期间修改它们可能会使其无效。
即使使用像 PDO 这样的数据库接口,通过在将参数发送到数据库之前替换参数来模拟准备好的语句,占位符值仍然是SQL 语句中包含的字符串。结果,SELECT FROM ?使用 mytable 作为参数最终会将 SELECT FROM 'mytable' 发送到数据库,使其无效。
降低风险
最安全的方法仍然是在字符串中使用 $mytable,但它必须附带一个用于检查用户输入的表白名单。这可以防止恶意行为者在任意表上执行 SQL 语句。因此,以下代码演示了安全实现:
if (whitelisted_tables($mytable)) { $statement = $mysqli->prepare("INSERT INTO $mytable VALUES (?,?,?,?,?,?,?);"); mysqli_stmt_bind_param( $statment, 'sssisss', $Partner, $Merchant, $ips, $score, $category, $overall, $protocol ); $statement->execute(); }
以上是如何在准备语句中安全地使用参数化表名来防止 SQL 注入?的详细内容。更多信息请关注PHP中文网其他相关文章!