更新1:
我收到以下错误:
Fatal error: Cannot pass parameter 2 by reference in /var/www/page1.php on line 42 Call Stack: 0.0008 341836 1. {main}() /var/www/page1.php:0
使用时:
if( $_SERVER['REQUEST_METHOD'] == 'POST') {
$fields = array(
'col1' => 'cb1',
'col2' => 'cb2',
'col3' => 'cb3',
'col4' => 'cb4',
);
$parts = array();
foreach($fields as $dbfield => $field)
$parts[] = '`' . $dbfield . '` = :' . $dbfield;
$dbh = new PDO('mysql:host=localhost;dbname=database', 'user', 'pass');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sth = $dbh->prepare('UPDATE `table1` SET ' . join(', ', $parts) . ' WHERE `id `= :id');
// temp simulation value
$id = 1;
$sth->bindParam(':id', $id, PDO::PARAM_INT, 4);
foreach($fields as $dbfield => $field)
$sth->bindParam(':' . $dbfield, isset($_POST[$field]) ? 1 : 0, PDO::PARAM_INT, 1);
$sth->execute();
}
原始问题:
以下代码是否会阻止SQL注入?
<?php
if( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
// all the way upto 50
$fields = array('col1'=>'cb1', 'col2'=>'cb2', 'col3'=>'cb3', 'col4'=>'cb4');
$update = '';
foreach($fields as $dbfield => $field) {
if ($update) $update.= ',';
$update.= ' '.$dbfield.'=';
$update .= isset($_POST[$field]) ? 1 : 0;
}
$DBH = new PDO( "mysql:host=localhost;dbname=database", "user", "pass" );
$DBH -> setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$STH = $DBH -> prepare( "update table1 set " . $update . " where id = :id" );
// temp simulation value
$id = 1;
$STH -> bindParam( ':id', $id, PDO::PARAM_INT, 4 );
$STH -> execute();
}
?>
<html>
<head>
<title></title>
</head>
<body>
<form method="post">
<input type="checkbox" name="cb1" />
<input type="checkbox" name="cb2" />
<input type="checkbox" name="cb3" />
<input type="checkbox" name="cb4" />
<!-- all the way to 50 -->
<input type="submit" value="submit" />
</form>
</body>
</html>
答案 0 :(得分:3)
嗯,在您的特定情况下,SQL注入无法通过此代码,因为您只是使用预先知道的列名1s和0来构建更新。但是,构建该更新的整体方式在其他情况下重复将是一种非常危险的做法。问题是,你正在使用PDO,但随后迫使它发送一个原始字符串,而不会丢弃你的数据,这会破坏PDO可以做的所有事情来保护你。
为了实际使用PDO针对SQL注入执行的操作,您需要使用?
或命名占位符来编写数据,并使用bindParam
来提供值。
使用命名参数绑定的动态构造示例可能如下所示:
if( $_SERVER['REQUEST_METHOD'] == 'POST') {
$fields = array(
'col1' => 'cb1',
'col2' => 'cb2',
'col3' => 'cb3',
'col4' => 'cb4',
);
$parts = array();
foreach($fields as $dbfield => $field)
$parts[] = '`' . $dbfield . '` = :' . $dbfield;
$dbh = new PDO('mysql:host=localhost;dbname=database', 'user', 'pass');
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sth = $dbh->prepare('UPDATE `table1` SET ' . join(', ', $parts) . ' WHERE `id `= :id');
// temp simulation value
$id = 1;
$sth->bindParam(':id', $id, PDO::PARAM_INT, 4);
foreach($fields as $dbfield => $field) {
$value = isset($_POST[$field]) ? 1 : 0;
$sth->bindParam(':' . $dbfield, $value, PDO::PARAM_INT, 1);
}
$sth->execute();
}
答案 1 :(得分:1)
由于您实际上根本没有将外国数据插入到您的表中,我会说您非常安全。 (你应该阅读别人的回答。我想不出每一种可能性。)
但是,我不知道你的代码中的占位符是什么用于简化示例。如果您确实选择使用实际用户数据,那么您应该开始正确使用PDO。截至目前,您只有一个占位符。您的所有字段都没有占位符。您已经有效地破坏了首先使用准备好的查询的目的。
如果您可以描述您想要做的事情,也许我们可以建议一个更清洁的解决方案。
答案 2 :(得分:-1)
这是动态SQL:
$STH = $DBH -> prepare( "update table1 set " . $update . " where id = :id" ).
除了通过针对已批准值的白名单检查输入$update
之外,无法使SQL注入动态SQL。
有关详细信息,请参阅此处:How to prevent SQL injection with dynamic tablenames?
您的代码安全吗? (是)强>
您的代码会在数组字段中输入值的白名单,如果输入匹配,则会注入1
,如果输入匹配,则会注入0
。
我看到没有SQL注入机会,但它看起来像代码味道。
建议清除代码气味
我个人将表重组为:
Simple_settings
---------------
new_id integer auto_increment primary key
user_id integer <<-- optional can be used to check if a user is allowed to alter
the setting.
page_id integer <<-- replaces your id
settingname varchar(20)
onoff boolean
现在您可以使用普通PDO更新您的表格:
UPDATE table1 onoff = :isfieldset WHERE settingname = :dbfield
AND page_id = :id
如果你有多个设置,你必须在一个循环中完成它们,但至少你的代码很简单,你的表格不会有很多列。
作为额外的奖励,如果设置数量发生变化,您无需更改表格布局
请注意如果恶意用户将自己的数据注入:dbfield update
将失败。
如果你使用它来插入,你的其余代码将不会读取该设置,所以你也应该没问题。
仍有一个问题
如果你有复选框(因为当前用户不允许更改所有设置)你的代码目前没有检查,如果有一些业务逻辑,你需要检查这个用户是允许进入这些设置。您可以在settings_table上的before update
触发器中执行此操作,或者对select
表使用(先前/已添加)user_allowed_settings
,以查看允许哪个用户设置什么设置。
答案 3 :(得分:-2)
不,因为即使您使用的是参数化查询/绑定变量,$update
变量仍然是由未经过验证的用户输入组成的。