3

We know that to prevent SQL injection problems string values must be escaped before the SQL query is composed -- particularly those from users or other external sources.

When should this escaping be done? Should it be done as the value enters the program, storing the escaped value for later use? Or should store the unescaped value, and escape it just as the query is being composed? Which approach is safer? What is the tradeoff?

1) example of escaping as the value is received:

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

2) example of escaping as the query is composed:

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

Is there a difference between these approaches? Which approach is more prone to injections, and what is the safest method to prevent it?

Community
  • 1
  • 1
telexper
  • 2,381
  • 8
  • 37
  • 66
  • 1
    http://phpsec.org/projects/guide/3.html – user1909426 Jan 05 '13 at 09:37
  • 1
    With regard to the session variable, that would all depend on how it was originally set. Anything originating from users should not be trusted. – PassKit Jan 05 '13 at 09:39
  • 2
    [The Great Escapism (Or: What You Need To Know To Work With Text Within Text)](http://kunststube.net/escapism/) – DCoder Jan 05 '13 at 09:39
  • 1
    This question is not a duplicate of the other! The other question ask how to escape values. This question asks a different important question: when should values be escaped? In other words: should you store the escaped values or escape them as needed. I have seen many programmers do this incorrectly. I'd be happy to help *rewrite* the question to make this clear, but I am new to StackOverflow and it is not clear to me whether I should do this, or just write a new question. That is: if the re-written question is NOT a duplicate of the other, can it be re-opened? – AgilePro Jan 07 '13 at 06:44
  • 2
    @BurntTooManyTimes your edit gets my approval and a re-open vote... thanks for your effort – Jon Clements Jan 07 '13 at 07:09
  • @BurntTooManyTimes I share your concern about closed topics but, as a matter of fact, in the end this question boils down to SQL injection problem. So, there is a link to the proper practice. While your `"We know that to prevent SQL injection problems values from outside the program must be escaped"` statement is simply wrong. Escaping is has nothing to do with injections and user input has nothing to do with escaping. – Your Common Sense Jan 07 '13 at 07:17
  • 1
    When you compose a SQL query as a string, you can not simply slap quotes around a string value. Instead, you must format the value according to SQL rules. This proper formatting is PRECISELY what prevents SQL injection problems. This question is simply about WHEN to do that formatting. Are you objecting to calling this "escaping"? Or are you objecting to the qualification that the value comes from outside (from a user)? – AgilePro Jan 07 '13 at 07:30
  • 2
    Who and why is someone downvoting every answer? – Salman A Jan 07 '13 at 08:08

5 Answers5

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.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • 1
    do you have any evidence that the real_escape_string function is insufficient. The documentation states that it is designed precisely for this purpose. I would love to see evidence that this is insufficient ... otherwise your statement is simply inflammatory and misleading. – AgilePro Jan 07 '13 at 18:12
  • +1 For recommending the use of PDO instead. The question is currently comparable to "If I'm going to stand in the middle of a busy motorway, is it best to look north or south?". The whole method of sanitising data and then manually crafting SQL statements is risky. @BurntTooManyTimes see this post for a deeper discussion with POCs http://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html – Dan Blows Jan 10 '13 at 10:08
  • I agree with @AgilePro. I hate people all over SO claiming to use prepared query ALL the time. Even php doesn't recommend that and says `mysqli_real_escape_string` is perfectly fine to use. Prepared queries have a performance hit and only makes sense when you have to use it multiple times, eg. importing a csv file. – Whip May 24 '22 at 03:39
  • @Whip the problem is, we are using prepared statements to protext from sql injections. While **nowhere** the manual page for mysqli_real_escape_string says it should be used for the purpose. Go figure. – Your Common Sense May 24 '22 at 06:13
  • Yeah but prepared statements is a lazy way of securing queries. Its primary purpose is to execute repeated queries. Securing the query is an added benefit on it. [This page](https://www.php.net/manual/en/security.database.sql-injection.php) has more options for doing that besides prepared statements and also mentions `mysqli_real_escape_string`. Hope it helps – Whip May 24 '22 at 06:29
  • @Whip thank you for pointing out that nonsense. Going to eradicate it right away. – Your Common Sense May 24 '22 at 06:55
  • @Whip to be clear, the problem is not in the function itself, but in the way it is understood by people. They tend to take it as a magic wand that somehow makes their query "safe", no matter what. While this humble string formatting function should never be used "to make data safe", which us a crystal clear nonsense. Although one can devise a complicated system that would employ this function along with other measures, it will be too complex and error-prone. And therefore, it's much better to use the "lazy", as you called it, solution. – Your Common Sense May 24 '22 at 07:34
0

You should escape them as late as possible.

The reason you would want to do this is so that your data is always exact. For example, if you escape the string right at the start, the strlen won't be the same (as the unescaped version), which could cause confusion/errors in some scenarios.


The real (imo) answer to your question is just to forget about escaping and use prepared statements'

Supericy
  • 5,866
  • 1
  • 21
  • 25
-1

It doesn't matter wether you escape the input before or in the query.

And YES everything has to be escaped. There is no reason, why you don't want to escape your own things.

If you don't want to escape the strings, you will realize it ;-)

Fabian Blechschmidt
  • 4,113
  • 22
  • 39
  • `everything has to be escaped` is **quite** inaccurate phrasing. "every string" it is. – Your Common Sense Jan 05 '13 at 11:23
  • every string is wrong to. Every string which is a parameter in an array might be right. If we are talking about a row name which is selected or ordered by. It is hard to explain, but there are a lot resources in the comments of the question. – Fabian Blechschmidt Jan 05 '13 at 13:00
-1

Values should never be escaped before actual use in composing the query. This is true whether you use a prepared statement/PDO or whether you use real_escape_string to compose the query as a SQL formatted string.

The practice of sanitizing/escaping a data value too early, and saving it in that form invites errors. If a variable contains a data value, like a customer name, or an account number, that variable should contain the raw value, un-escaped.

It is only when you actually form query, that should you make sure that all the values are properly encoded as they are put into that query.

Think of the variables holding raw data values as a different type of variable from the variables holding the queries. Never assign a query value directly to a raw data value, and never combine a raw data value to make a query. The composing of the query is the trigger to know that you should encode the raw data values.

By making this a practice, it will be clear and consistent where this encoding happens, you will reduce the potential of double encoding or failure to encode.

Imagine that you attempt to do the opposite: to pre-encode all values. This is not really possible since you have many many string values, and not all of them are to be used in queries. Somewhere, you are likely to have a variable that is used both as output to the display as well as used for a query. Displaying the escaped value would be incorrect. It is similarly difficult (or impossible) to track which variables are meant for use in a query, and which are meant for other, non-SQL uses.

Always store raw (un-escaped) values in all string variables until you actually compose a query. This practice is consistent with using prepared statements as well, since a prepared statement is passed an un-escaped value.

AgilePro
  • 5,588
  • 4
  • 33
  • 56
  • Anyone care to comment on why the down votes? This speaks directly to the issue of why it is problematic to call the escape method BEFORE you are composing the query. – AgilePro Jan 07 '13 at 07:44
  • All answers where downvoted, even the answer mentioning PDO, so i'm not sure whether the downvoter even read the answers. – martinstoeckli Jan 07 '13 at 08:30
  • -1 because this gets really messy. It depends on character encoding, it's difficult to maintain, and it's unnecessary since you can just use PDO instead. Chris Shifflet likened `real_escape_string()` to `addslashes()`. – Dan Blows Jan 10 '13 at 10:15
  • OK, but using PDO you certainly DON'T want to escape the data and store the escaped data in a variable, which is the point of this answer. In any case the question was specifically about when to use real_escape_string and voting down an answer that suggests you don't use it early makes it look like you favor using real_escape_string early. – AgilePro Jan 10 '13 at 15:52
-2

First of all you should pass the connection as the second parameter in mysqli real escape string Second of all you should also use prepared statements

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

meWantToLearn
  • 1,691
  • 2
  • 25
  • 45