3

我们知道,为了防止SQL 注入问题,必须在编写 SQL 查询之前对字符串值进行转义——尤其是来自用户或其他外部来源的字符串值。

什么时候应该进行这种转义?是否应该在值进入程序时完成,存储转义值以供以后使用?还是应该存储未转义的值,并在编写查询时对其进行转义?哪种方法更安全?权衡是什么?

1) 收到值时转义的示例:

$test = $mysqli->real_escape_string($_POST['test']);
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $test . "'";

2)组成查询时转义的示例:

$test = $_POST['test'];
. 
. 
. 
$query=" UPDATE * from test_panel where test='" . $mysqli->real_escape_string('$test') . "'";

这些方法之间有区别吗?哪种方法更容易注射,最安全的预防方法是什么?

4

5 回答 5

1

That's quite interesting question but the answer is not that easy.

What is the proper time to use real_escape_string? When data arrives in POST, or just before composing the query?

NEITHER

Let me explain it a bit.

First, let's sort out the terminology. There are many mistakes in the way the question put in.

  1. Let's talk not of escaping using real_escape_string but rather of formatting. Just because escaping has a very limited use - it's only a part of the formatting rules of just one type of SQL literals. While other types require different formatting rules.
  2. Therefore, formatting when data "arrives in POST" is out of question - we just can't tell which field is going into which position in the query and thus we just don't know which rules to apply.
  3. Last but not least: nor POST nor any other external source has absolutely nothing to do with query formatting. Once you have to put a string literal into query, you have to format it according to SQL syntax rules, no matter of it's source. Same goes for the numbers and such.

So, the only proper time when we have to format our data is right before the query composing.

Yet applying real_escape_string() right in the application code is a very bad practice.

  1. As it was mentioned above, escaping is insufficient to format a string. String formatting involves both escaping and quoting. So, whatever facility intended to format strings for the SQL query, it should always perform both tasks, not one. Both quoting and escaping. Because these 2 rules are totally useless if applied one without another. So, it's essential to couple them together, in one facility.
  2. Don't forget of different formatting rules for different data types. Numbers have to be cast to it's type explicitly, while escaping will do no good for them.
  3. Manual escaping is just silly. Repeated $mysqli->real_escape_string('$test') makes your code bloated and hard to read. Why not to ask a database driver to do all the formatting for you? So, you have to follow the most modern technology - use a placeholder to represent data in the query. While processing such a placeholder, driver will automatically format the data going on it's place.
    And it will be either safe and convenient.

There are 2 methods of using placeholders easy way (without manual binding which is no better than manual escaping in terms of readability):

  • Use PDO, as it lets you just pass a variable to be used in the prepared query

so, the code going to be

$db->prepare("SELECT * from test_panel where test=?");
$db->execute(array($_POST['test']));

and PDO will do all the formatting internally

  • or invent your own wrapper to implement placeholders

like this one

function paraQuery()
{
    global $mysqli;

    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = $mysqli->real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = $mysqli->query($query);
    if (!$result)
    {
        throw new Exception($mysqli->error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

or, for your current query:

$result = paraQuery("SELECT * from test_panel where test=%s", $_POST['test']);

look - it become short, sane and safe.

于 2013-01-05T11:51:10.447 回答
0

你应该尽可能晚地逃脱它们。

您想要这样做的原因是您的数据始终是准确的。例如,如果您在一开始就对字符串进行转义,strlen则不会相同(与未转义的版本相同),这可能会在某些情况下导致混淆/错误。


您问题的真正(imo)答案就是忘记转义并使用prepared statements'

于 2013-01-05T09:49:25.850 回答
-1

无论您在查询之前还是在查询中转义输入都没有关系。

是的,一切都必须逃脱。没有理由,为什么不想逃避自己的事情。

如果你不想逃避字符串,你会意识到它;-)

于 2013-01-05T09:42:31.140 回答
-1

在实际用于编写查询之前,绝不应转义值。无论您使用准备好的语句/PDO 还是使用 real_escape_string 将查询组合为 SQL 格式的字符串,这都是正确的。

过早清理/转义数据值并以这种形式保存它的做法会导致错误。如果变量包含数据值,例如客户姓名或帐号,则该变量应包含未转义的原始值。

只有当您实际形成查询时,您才应该确保所有值在放入该查询时都已正确编码。

将保存原始数据值的变量视为与保存查询的变量不同类型的变量。永远不要将查询值直接分配给原始数据值,也不要结合原始数据值进行查询。查询的组成是了解您应该对原始数据值进行编码的触发器。

通过实践,这种编码发生的位置将变得清晰且一致,您将减少双重编码或编码失败的可能性。

想象一下,您尝试做相反的事情:对所有值进行预编码。这实际上是不可能的,因为您有很多字符串值,并且并非所有这些值都将在查询中使用。在某个地方,您可能有一个变量既可用作显示器的输出,也可用于查询。显示转义值是不正确的。同样难以(或不可能)跟踪哪些变量用于查询,哪些变量用于其他非 SQL 用途。

始终在所有字符串变量中存储原始(未转义)值,直到您实际编写查询。这种做法也与使用准备好的语句一致,因为准备好的语句传递了一个未转义的值。

于 2013-01-05T10:21:22.210 回答
-2

首先,您应该将连接作为 mysqli 真实转义字符串中的第二个参数传递其次,您还应该使用准备好的语句

http://php.net/manual/en/mysqli.prepare.php

于 2013-01-05T09:49:29.030 回答