安全地使用准备好的语句来查询数据库

发布于 2024-08-20 16:14:56 字数 649 浏览 11 评论 0原文

我正在尝试编写一个在允许进行的查询中多功能的函数,而且可以安全地防止注入< /强>。下面的代码按原样抛出错误,但如果我使用“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() 看起来有点像倒退。有什么想法吗?

I'm trying to write a function that is versatile in the queries it is allowed to make, but also safe from injection. The code below throws an error as is, but if I run it with 'name' instead of ':field' it works fine.

$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";
      }
    }  
  }

How would I allow the table/fields/values to be changed, without allowing injection attacks? mysql_real_escape_string() seems kind of like a step backwards. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

ゞ记忆︶ㄣ 2024-08-27 16:14:56

我可能是错的,但我不相信您可以在 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

I may be mistaken, but I don't believe you can supply fields as parameters in PDO.

Why not just specify it as argument to the function? Unlike the data being supplied by the user, the fields are finite, well defined and don't change often. As in

selectquery('name',$value);

and have your query be

$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  

Since you're supplying the field name for the function call yourself, this is safe unless you're worried you are going to attack yourself with SQL injection.

If for some odd reason the name of the field is coming from user input, you could make an array of allowed fields. That way, you're safe from injection because the values can only come from your array. I don't know why the field name would be coming from user input and thus be untrusted, unless perhaps you're making an API? Other wise there's probably a better way to achieve the goal.

Anyhow, this would be a potential solution, to use a whitelist for table names:

$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
马蹄踏│碎落叶 2024-08-27 16:14:56

使用 MDB2 自动执行
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());
}
?>

Use 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());
}
?>
埋葬我深情 2024-08-27 16:14:56

数据库标识符(列名、表名和数据库名)不能也不应该被转义,因此不能在 SQL 准备的查询中使用它们。

有时您可能需要反引号这些标识符(对于 MySQL 使用 `,对于 SQLite 使用 ")。

Database identifiers (column names, table names and database names) can not and should not be escaped, therefore you can't use them in SQL prepared queries.

Sometimes you might need to backtick those identifiers though (use ` for MySQL and " for SQLite).

落墨 2024-08-27 16:14:56

绑定变量将其绑定为数据,特别是为了防止它更改查询的语法。此外,固定语法允许引擎分析一次准备好的查询,然后针对每组值快速运行它们。我鼓励您不要在 SQL 之上构建一个手持层,但如果您必须这样做,请考虑 preg_replace('/\W/', '', $field)。

Binding a variable binds it as data, specifically to prevent it from changing the syntax of the query. Furthermore, having a fixed syntax allows engines to analyze prepared queries once and then run them quickly for each set of values. I would encourage you not to build a hand-holding layer on top of SQL, but if you must, consider a preg_replace('/\W/', '', $field).

锦爱 2024-08-27 16:14:56

不幸的是,PHP 数据对象没有公开引用字段标识符的方法。

作为替代方案,PEAR::MDB2(PHP 数据对象的精神前身)有一个 < a href="http://pear.php.net/package/MDB2/docs/2.4.1/MDB2/MDB2_Driver_Common.html#methodquoteIdentifier" rel="nofollow noreferrer">->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 没有提供安全的方法来完成您想做的事情。

PHP Data Objects unfortunately doesn't expose a method to quote a field identifier.

As an alternative, PEAR::MDB2 (spiritual predecessor to PHP Data Objects) has a ->quoteIdentifier() option which allows you to achieve what you want in a safe manner.

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";
      }
    }  
  }

I understand that this solution is less than optimal (changing abstraction layer in the middle of developing a project is cumbersome) but unfortunately, PDO provides no safe way of doing what you want to do.

哎呦我呸! 2024-08-27 16:14:56

支持 Andrew Moore 的回应:唯一的方法是标识符引用,而 PDO 没有提供必要的方法。您可能只想借用其标识符引用的实现,而不是使用 MDB2。该函数非常简单,您应该能够编写自己的函数并相当容易地检查它的错误。

  1. . 上的输入字符串拆分为部分列表(可能只有一个)

  2. 对于每个部分:

    1. 将所有 ` 替换为 ``
    2. 在开头和结尾添加 `,除非该部分为空。*
  3. 使用 连接各部分。

例如,quote_identifier("一二.三") 应该是 `一二`.`三` ——非常简单。

为了额外的安全性,您还可以验证字符串不包含任何非法字符,即使在带引号的标识符中也是如此(特别是空值,请参阅 MySQL 文档),但实际上 MySQL 应该捕获这些。 MDB2 不打扰。

*:此检查是必要的,因为 .columnname 是合法的,并且应该引用 .`columnname` 而不是 ``.`列名`

Seconding Andrew Moore's response: the only way to go is identifier quoting, and PDO doesn't provide the necessary method. Rather than use MDB2, you might just want to borrow its implementation of identifier quoting. The function is simple enough that you should be able to write your own and vet it for bugs fairly easily.

  1. Split the input string on . into a list of parts (there might be only one)

  2. For each part:

    1. Replace all ` with ``.
    2. Add a ` to the beginning and to the end unless the part is empty.*
  3. Join the parts with ..

As an example, quote_identifier("one two.three") should be `one two`.`three` -- pretty straightforward.

For extra safety you could also verify that the string doesn't contain any characters that are illegal even in quoted identifiers (particularly nulls, see the MySQL docs) but in truth MySQL should catch those. MDB2 doesn't bother.

*: This check is necessary because .columnname is legal, and should quote to .`columnname` and not ``.`columnname`.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文