Does eliminating dangerous characters avoid SQL-injection?

♀尐吖头ヾ 提交于 2019-12-01 04:02:35
Gumbo

To be able to inject arbitrary SQL from the context of a string literal, that string literal needs to be left. This is only possible by introducing a string end delimiter, in this case a single ', or by expand the a string literal to a preceding ', e.g., by using the escapes character \:

$a = '\\';
$b = ' OR 1=1 OR ';
$c = ' --';

$query = "SELECT * FROM t1 WHERE a='$a' AND b='$b' AND c='$c'";
// result:
// SELECT * FROM t1 WHERE a='\' AND b=' OR 1=1 OR ' AND c=' --'
//                          \_________/           \_______/

Now as your function removes any ' and \, it seems to be impossible to leave or expand the string literal and thus not possible to inject arbitrary SQL.

However, since your function does not take the actual character encoding into account, it is possible to exploit this if the MySQL’s character encoding is GBK, similar to how it can be exploited when using addslashes instead of mysql_real_escape_string:

$a = "\xbf";
$b = " OR 1=1 OR ";
$c = " --";

$query = "SELECT * FROM t1 WHERE a='$a' AND b='$b' AND c='$c'";
// result:
// SELECT * FROM t1 WHERE a='縗 AND b=' OR 1=1 OR ' AND c=' --'
//                          \_________/           \_______/

So to play safe, use mysql_real_escape_string or other proven methods to prevent SQL injections.

Your Common Sense

The very idea of removing whatever characters is utterly wrong.

That's what essentially wrong your approach.

You have to format your SQL literals properly instead of spoiling them.

Imagine this very site were using such a "protection": you'd were unable to post your question!

To answer your question literally - yes, under some circumstances it's very easy to inject. Just because the very idea of all-in-once sanitization is broken. PHP had a similar feature once, called "magic quotes". It was a hard lesson, but now it's got removed from the language at last. For the very reasons I told you:

  • it does not make "data" "secure"
  • it spoils your data instead

Every SQL literal have to be treated personally, according to its role.

The right way to prevent SQL injection is by using parameterized queries. This means defining the SQL code that is to be executed with placeholders for parameter values, programmatically adding the parameter values, then executing the query. Doing this allows the server to create an execution plan for the query, which prevents any "injected" SQL from being executed. An example will help in explaining this. Let’s use the same script, but I’ll define the SQL query with parameter placeholders:

    $sql = "SELECT * FROM UserTbl WHERE Username = ? and Password = ?";

Now, I’ll define an array that holds the parameter values:

    $params = array($_POST['Username’], $_POST['Password’]);

When I execute the query, I pass the $params array as an argument:

    $stmt = sqlsrv_query($conn, $sql, $params);

When sqlsrv_query is called, an execution plan is created on the server before the query is executed. The plan only allows our original query to be executed. Parameter values (even if they are injected SQL) won’t be executed because they are not part of the plan. So, if I submit a password like I did in the example above ('or 1=1--), it will be treated as user input, not SQL code. In other words, the query will look for a user with this password instead of executing unexpected SQL code.

The script above, modified to prevent SQL injection, looks like this:

    <form method="post" action="injection.php" enctype="multipart/form-data" >
        Username:<input type="text" name="Username" id="Username"/></br>
        Password:<input type="text" name="Password" id="Password"/></br>
        <input type="submit" name="submit" value="Submit" />
    </form>
    <?php
    $params = array($_POST['Username'], $_POST['Password']);


    $server = "MyServer\sqlexpress";
    $options = array("Database"=>"ExampleDB", "UID"=>"MyUID", "PWD"=>"MyPWD");
    $conn = sqlsrv_connect($server, $options);
    $sql = "SELECT * FROM UserTbl WHERE Username = ? and Password = ?";
    $stmt = sqlsrv_query($conn, $sql, $params);
    if(sqlsrv_has_rows($stmt))
    {
        echo "Welcome.";
    }
    else
    {
        echo "Invalid password.";
    }
    ?>

Note: If you expect to execute a query multiple times with different parameter values, use the sqlsrv_prepare and sqlsrv_execute functions. The sqlsrv_prepare function creates an execution plan on the server once and the sqlsrv_execute function executes the query with different parameter values each time it is called.

ose

Yes, this can be defeated using Unicode characters and manipulating character sets.

See https://security.stackexchange.com/questions/11391/how-did-anonymous-use-utf-16-ascii-to-fool-php-escaping for further details.

Femaref

Your function doesn't deal with different encodings.

Don't try to come up with sanitation methods yourself, use something already made. In the case of mysql_*, it would be mysql_real_escape_string, however, you shouldn't use mysql_* anymore, use PDO or mysqli instead.

See: How can I prevent SQL injection in PHP? for further details.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!