提问人:Nicolás Bustos 提问时间:4/4/2019 最后编辑:Nicolás Bustos 更新时间:4/4/2019 访问量:1667
如何动态准备 SQL 查询(包括列名)以避免 SQL 注入
How to prepare SQL query dynamically (column names too) avoiding SQL injection
问:
我最近了解了 SQL 注入和避免它的 PHP 建议,使用 和 .
现在,我想动态准备 SQL 查询,添加列名和值。prepare()
bind_param()
我打算这样做,使HTML输入的字段与MySQL数据库列同名。name
<input type="text" name="firstname" >
<input type="text" name="lastname" >
然后,使用 mysqli 动态创建 SQL 查询。
// Extract values from POST
$parameters = $_POST;
// Organize the values in two strings
foreach ($parameters as $id => $value) {
$fields = $fields . "`" . $id . "`,";
$values = $values . "'" . $value . "',";
/*e.g.
$fields = `firstname`,`lastname`
$values = 'John','Wick'
*/
}
// Write into the database
$sql = "INSERT INTO `user` ($fields) VALUES ($values)";
/*e.g.
INSERT INTO `user` (`firstname`,`lastname`) VALUES ('John','Wick')
*/
我想知道是否有办法使用并避免SQL注入来做到这一点,可能会在HTML输入标签中添加一些,或者是否有更好,更最佳实践的方法来做到这一点。prepare()
bind_param()
data-type="s"
答:
-1赞
Randy Fumigator Whitaker
4/4/2019
#1
我注意到您在问题中包含了 mysqli 标签,因此假设您的数据库是 MySQL 并且您正在使用本机 MySQL 函数,那么您可以执行以下操作:
$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, 'sssd', $code, $language, $official, $percent);
$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;
/* execute prepared statement */
mysqli_stmt_execute($stmt);
是的,我直接从 mysqli_stmt_bind_param 的 PHP 手册页中撕下了它。
评论
1赞
Nicolás Bustos
4/4/2019
感谢您的快速回答!但是,我想创建 SQL 语句,添加列名(如、所需数量的符号)和数据类型字符串(例如使用循环或函数),而不是像您的示例那样以静态方式创建准备好的查询。CountryLanguage
?
'sssd'
6赞
Bill Karwin
4/4/2019
#2
只能对常量值(带引号的字符串、带引号的日期时间或数值文本)的元素使用绑定参数。
不能将参数占位符用于 SQL 中的任何其他内容,例如列名、表名、值列表、SQL 关键字或表达式或其他语法。
如果需要使列名动态化,唯一的选择是根据已知列列表对其进行验证。
$columns_in_user_table = [
'userid'=>null,
'username'=>'',
'firstname'=>'',
'lastname'=>''
];
// Extract values from POST, but only those that match known columns
$parameters = array_intersect_key($_POST, $columns_in_user_table);
// Make sure no columns are missing; assign default values as needed
$parameters = array_merge($columns_in_user_table, $parameters);
如果使用 PDO 而不是 mysqli,则可以跳过绑定。只需使用命名参数,并将列值对的关联数组直接传递给:execute()
$columns = [];
$placeholders = [];
foreach ($parameters as $col => $value) {
$columns[] = "`$col`";
$placeholders[] = ":$col";
}
$column_list = implode($columns, ',');
$placeholder_list = implode($placeholders, ',');
// Write into the database
$sql = "INSERT INTO `user` ($column_list) VALUES ($placeholder_list)";
$stmt = $pdo->prepare($sql);
$stmt->execute($parameters);
评论
0赞
Nicolás Bustos
4/5/2019
谢谢!这就是我需要的。我将切换到 PDO。我有疑问:由于我们在行中使用 name 参数,因此是否不需要数组具有 的形式,在列名之前带有冒号?:$col
$parameters
array(':firstname' => 'John',':lastname' => 'Wick')
0赞
Bill Karwin
4/5/2019
绑定参数键中的冒号是多年前需要的,但我认为有人意识到没有理由这样做,并且将 $_POST 数组直接转换为所需的参数变得更加工作。我忘了哪个版本的 PHP 更改了它,但只要您不使用生命周期结束的 PHP 版本,您就不需要在绑定名称中添加冒号。它们仅在 SQL 查询中是必需的。
上一个:为字段分配名称 SQL
评论