如何在我的查询中不使用原始字段名称?
How not to use raw field names in my query?
我正在研究 PDO 并尝试更简单地使用插入和更新查询。我之前的问题得到了很好的答案,但他们也指出我的查询中存在严重的注入问题。我试过自己弄清楚,但并不像我想的那样顺利。我怎样才能消除这些查询中的注入问题?请帮我!
function pdoSet($fields, &$values, $source = array()){
$set = '';
$values = array();
if(!$source) $source = &$_POST;
foreach($fields as $field){
if(isset($source[$field])){
$set .= " $field =:$field, ";
$values[$field] = $source[$field];
}
}
return substr($set, 0, -2);
}
$fields = array(
'name'
, 'part'
, 'tel'
, 'email'
, 'title'
, 'contents'
);
if(!$idx){
$fields[] = 'reg_date';
$values[] = 'now()';
$st = $pdo -> prepare("insert into qna_board set ".pdoSet($fields, $values));
$st->execute($values);
}else{
$st = $pdo -> prepare("update qna_board set ".pdoSet($fields, $values)." where idx = :idx");
$st ->bindParam(":idx", $idx);
$st->execute($values + compact('idx'));
}
起初您的代码看起来确实容易 SQL 注入(正如 Deceze 在 中指出的那样),但经过仔细检查后我非常确定它是安全的。
看起来麻烦的部分是:
foreach($fields as $field){
if(isset($source[$field])){
$set .= " $field =:$field, ";
$values[$field] = $source[$field];
}
}
您正在动态生成 column => placeholder
对,看起来这些值来自 $_POST
超全局,但它们不是。您传递给 pdoSet()
的第一个参数始终是一个硬编码数组,而不是基于保护您免受注入的用户输入。
附带说明一下,我会稍微重新考虑一下您的代码设计。你说你只是在学习 PDO(我为之鼓掌,它是一个很棒的驱动程序),所以这里有一个非常简单的 OOP 片段,它更清晰一些,你可以以此为基础:
class FooBarBaz
{
/**
* @var PDO
*/
protected $pdo;
/**
* @param string $index
* @param array $input_values
* @return bool
*/
public function update($index = null, Array $input_values = null)
{
if (is_null($input_values))
{
$input_values = $_POST;
}
$column_list = [
'name',
'part',
'tel',
'email',
'title',
'contents',
];
$placeholders = [];
$query_values = [];
foreach ($column_list as $column_name)
{
if (array_key_exists($column_name, $input_values))
{
$placeholders[] = sprintf('`%s` = :%s', $column_name, $column_name);
$query_values[$column_name] = $input_values[$column_name];
}
}
if (isset($index))
{
$sql = sprintf('UPDATE qna_board SET %s WHERE idx = :idx', implode(',', $placeholders));
$query_values['idx'] = $index;
}
else
{
$sql = sprintf('INSERT INTO qna_board SET %s, reg_date = NOW()', implode(',', $placeholders));
}
return $this->pdo->prepare($sql)->execute($query_values);
}
}
也不是我按字面意思指定 SQL 函数 NOW()
的方式。这是因为这样的数据必须在解析时传递给 PDO (see this answer)。请注意,您还应该检查 errors/exceptions(您应该配置 PDO 以使用后者)并适当地处理它们。一个好的 DBAL 也会从程序员那里抽象出很多 "guts" PDO,这通常是一件好事(当然,一旦你练习了基础知识)。
我正在研究 PDO 并尝试更简单地使用插入和更新查询。我之前的问题得到了很好的答案,但他们也指出我的查询中存在严重的注入问题。我试过自己弄清楚,但并不像我想的那样顺利。我怎样才能消除这些查询中的注入问题?请帮我!
function pdoSet($fields, &$values, $source = array()){
$set = '';
$values = array();
if(!$source) $source = &$_POST;
foreach($fields as $field){
if(isset($source[$field])){
$set .= " $field =:$field, ";
$values[$field] = $source[$field];
}
}
return substr($set, 0, -2);
}
$fields = array(
'name'
, 'part'
, 'tel'
, 'email'
, 'title'
, 'contents'
);
if(!$idx){
$fields[] = 'reg_date';
$values[] = 'now()';
$st = $pdo -> prepare("insert into qna_board set ".pdoSet($fields, $values));
$st->execute($values);
}else{
$st = $pdo -> prepare("update qna_board set ".pdoSet($fields, $values)." where idx = :idx");
$st ->bindParam(":idx", $idx);
$st->execute($values + compact('idx'));
}
起初您的代码看起来确实容易 SQL 注入(正如 Deceze 在
看起来麻烦的部分是:
foreach($fields as $field){
if(isset($source[$field])){
$set .= " $field =:$field, ";
$values[$field] = $source[$field];
}
}
您正在动态生成 column => placeholder
对,看起来这些值来自 $_POST
超全局,但它们不是。您传递给 pdoSet()
的第一个参数始终是一个硬编码数组,而不是基于保护您免受注入的用户输入。
附带说明一下,我会稍微重新考虑一下您的代码设计。你说你只是在学习 PDO(我为之鼓掌,它是一个很棒的驱动程序),所以这里有一个非常简单的 OOP 片段,它更清晰一些,你可以以此为基础:
class FooBarBaz
{
/**
* @var PDO
*/
protected $pdo;
/**
* @param string $index
* @param array $input_values
* @return bool
*/
public function update($index = null, Array $input_values = null)
{
if (is_null($input_values))
{
$input_values = $_POST;
}
$column_list = [
'name',
'part',
'tel',
'email',
'title',
'contents',
];
$placeholders = [];
$query_values = [];
foreach ($column_list as $column_name)
{
if (array_key_exists($column_name, $input_values))
{
$placeholders[] = sprintf('`%s` = :%s', $column_name, $column_name);
$query_values[$column_name] = $input_values[$column_name];
}
}
if (isset($index))
{
$sql = sprintf('UPDATE qna_board SET %s WHERE idx = :idx', implode(',', $placeholders));
$query_values['idx'] = $index;
}
else
{
$sql = sprintf('INSERT INTO qna_board SET %s, reg_date = NOW()', implode(',', $placeholders));
}
return $this->pdo->prepare($sql)->execute($query_values);
}
}
也不是我按字面意思指定 SQL 函数 NOW()
的方式。这是因为这样的数据必须在解析时传递给 PDO (see this answer)。请注意,您还应该检查 errors/exceptions(您应该配置 PDO 以使用后者)并适当地处理它们。一个好的 DBAL 也会从程序员那里抽象出很多 "guts" PDO,这通常是一件好事(当然,一旦你练习了基础知识)。