我正在尝试在允许的查询中编写 多才多艺 的函数,但 安全注入 即可。下面的代码按原样抛出错误,但是如果我使用'name'而不是':field'运行它,它可以正常工作。
$field = "name";
$value = "joe";
function selectquery($field, $value)
{
global $dbcon;
$select = $dbcon->prepare('SELECT * FROM tester1 WHERE :field = :value');
if($select->execute(array(':field' => $field, ':value' => $value)));
{
$row = $select->fetch();
for ($i=0; $i<3; $i++)
{
echo $row[$i]."\n";
}
}
}
如何在不允许注入攻击的情况下允许更改表/字段/值? mysql_real_escape_string()似乎有点像向后退一步。有什么想法吗?
答案 0 :(得分:3)
我可能会弄错,但我不相信你可以在PDO中提供字段作为参数。
为什么不将它指定为函数的参数?与用户提供的数据不同,字段是有限的,定义良好且不经常更改。如在
selectquery('name',$value);
并让您的查询
$field = "name";
$value = "joe";
function selectquery($field, $value)
{
global $dbcon;
$select=$dbcon->prepare("SELECT * FROM tester1 WHERE $field = :value");
if($select->execute(array(':value' => $value)));
//etcetera
由于您自己为函数调用提供字段名称,因此除非您担心自己会使用SQL注入攻击自己,否则这是安全的。
如果由于某些奇怪的原因,该字段的名称来自用户输入,您可以创建一个允许字段数组。这样,您就可以安全地进行注射,因为这些值只能来自您的数组。我不知道为什么字段名称来自用户输入,因此不受信任,除非您正在制作API?其他方面,可能有更好的方法来实现目标。
无论如何,这可能是一个潜在的解决方案,使用白名单来表名:
$field = "name";
$value = "joe";
$allowed_fields=array('name','other_name','sandwich');
function selectquery($field_name, $value)
{
global $dbcon,$allowed_fields;
if(!in_array($field_name,$allowed_fields)){ return false; }
else{ $field=$field_name; }
$select=$dbcon->prepare("SELECT * FROM tester1 WHERE $field = :value");
if($select->execute(array(':value' => $value)));
//etcetera
答案 1 :(得分:1)
使用MDB2 autoExecute
http://pear.php.net/manual/en/package.database.mdb2.intro-auto.php
<?php
// Once you have a valid MDB2 object named $mdb2...
$table_name = 'user';
$fields_values = array(
'id' => 1,
'name' => 'Fabien',
'country' => 'France'
);
$types = array('integer', 'text', 'text');
$mdb2->loadModule('Extended');
$affectedRows = $mdb2->extended->autoExecute($table_name, $fields_values,
MDB2_AUTOQUERY_INSERT, null, $types);
if (PEAR::isError($affectedRows)) {
die($affectedRows->getMessage());
}
?>
答案 2 :(得分:0)
数据库标识符(列名,表名和数据库名)不能也不应该被转义,因此您不能在SQL准备的查询中使用它们。
有时候你可能需要反推这些标识符(对MySQL使用`,对SQLite使用“)。
答案 3 :(得分:0)
绑定变量将其绑定为数据,特别是阻止更改查询的语法。此外,具有固定语法允许引擎分析准备好的查询一次,然后针对每组值快速运行它们。我建议你不要在SQL之上构建一个手持层,但如果必须,可以考虑一个preg_replace('/ \ W /','',$ field)。
答案 4 :(得分:0)
遗憾的是,PHP数据对象没有公开引用字段标识符的方法。
作为替代方案,PEAR::MDB2(PHP数据对象的精神前身)有->quoteIdentifier()
选项,可让您以安全的方式实现所需。
function selectquery($field, $value)
{
global $dbcon;
$select = $dbcon->prepare('SELECT * FROM tester1 WHERE ' . $dbcon->quoteIdentifier($field) . ' = :value');
if($select->execute(array('field' => $field, 'value' => $value)));
{
$row = $select->fetchRow();
for ($i=0; $i<3; $i++)
{
echo $row[$i]."\n";
}
}
}
我知道这个解决方案不是最优的(在开发项目的过程中更改抽象层很麻烦)但不幸的是,PDO没有提供做你想做的事情的安全方法。
答案 5 :(得分:0)
借调Andrew Moore的回答:唯一的方法是标识符引用,PDO不提供必要的方法。您可能只想借用其标识符引用的实现,而不是使用MDB2。该功能非常简单,您应该能够自己编写并轻松地查找错误。
将.
上的输入字符串拆分为部分列表(可能只有一个)
每个部分:
`
替换为``
。`
,除非该部分为空。 * 使用.
加入部件。
例如,quote_identifier("one two.three")
应为`one two`.`three`
- 非常简单。
为了更加安全,您还可以验证字符串是否包含任何非法的字符,即使在带引号的标识符中也是如此(特别是空值,请参阅the MySQL docs)但实际上MySQL应该捕获这些字符。 MDB2没有打扰。
* :此检查是必要的,因为.columnname
是合法的,应引用.`columnname`
而非``.`columnname`
。