消除危险字符是否可以避免SQL注入?

时间:2013-06-01 13:10:02

标签: php security sql-injection

有许多出色的方法来保护应用程序免受SQL注入的攻击,例如,此问答表示某些方式How to prevent SQL injection in PHP?

这个问题是关于已经编写的申请。我们不想修改它以提高其安全性。我们想知道所使用的方法是否容易受到注入攻击。

我们需要知道这部分代码是否是泄漏数据的原因?

这是一个PHP函数,可以消除一些无用的(对我们而言)和可能有害的字符:

function removeBadCharacters($s)
{
   return str_replace(array('&','<','>','/','\\','"',"'",'?','+'), '', $s);
}

它抛出危险的字符以避免SQL注入攻击(不要担心删除的字符,例如&<>/\"'?+应用程序不需要它们)

$x = removeBadCharacters($_POST['data']);

mysql_query("insert into table (x) values ('".$x."');");

mysql_query("select * from into where name = '".$x."';"); 
  • 是否足以使查询安全?

  • 有没有办法绕过removeBadCharacters

  • 如何破解?

5 个答案:

答案 0 :(得分:9)

为了能够从string literal的上下文中注入任意SQL,需要保留该字符串文字。这只能通过引入字符串结束分隔符(在本例中为单个')或通过将字符串文字扩展为前面的'来实现,例如,通过使用转义字符\

$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=' --'
//                          \_________/           \_______/

现在,当您的函数删除任何'\时,似乎无法离开或扩展字符串文字,因此无法注入任意SQL。< / p>

但是,由于您的函数没有考虑实际的字符编码,如果MySQL的字符编码是GBK,可以利用它,类似于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=' --'
//                          \_________/           \_______/

为了安全起见,use mysql_real_escape_string or other proven methods to prevent SQL injections

答案 1 :(得分:5)

删除任何字符的想法是完全错误的。

这就是你的做法本质上是错误的。

您必须正确格式化您的SQL文字,而不是破坏它们。

想象一下,这个网站正在使用这样的“保护”:你无法发布你的问题!

从字面上回答你的问题 - 是的,在某些情况下,它很容易注入。仅仅因为一次性清理的想法被打破了。 PHP有一个类似的功能,称为“魔术引号”。这是一个艰难的教训,但现在它终于从语言中删除了。由于我告诉你的原因:

  • 它不会使“数据”“安全”
  • 它会破坏您的数据

根据其角色,每个SQL文字都必须经过个人处理。

答案 2 :(得分:3)

防止SQL注入的正确方法是使用参数化查询。这意味着定义将使用参数值的占位符执行的SQL代码,以编程方式添加参数值,然后执行查询。这样做允许服务器为查询创建执行计划,从而阻止执行任何“注入的” SQL。一个例子将有助于解释这一点。我们使用相同的脚本,但是我将使用参数占位符定义SQL查询:

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

现在,我将定义一个包含参数值的数组:

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

执行查询时,我将$ params数组作为参数传递:

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

调用sqlsrv_query时,将在执行查询之前在服务器上创建执行计划。该计划仅允许执行我们的原始查询。参数值(即使它们是注入的SQL)也不会执行,因为它们不是计划的一部分。因此,如果我像上面的示例一样提交密码(“或1 = 1--”),它将被视为用户输入,而不是SQL代码。换句话说,查询将查找具有此密码的用户,而不是执行意外的SQL代码。

上面的脚本经过修改以防止SQL注入,如下所示:

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

注意:如果希望使用不同的参数值多次执行查询,请使用sqlsrv_prepare和sqlsrv_execute函数。 sqlsrv_prepare函数在服务器上创建一次执行计划,并且sqlsrv_execute函数每次调用时都会使用不同的参数值执行查询。

答案 3 :(得分:2)

您的功能不处理不同的编码。

不要试图自己想出卫生方法,使用已经制作的东西。对于mysql_*,它将是mysql_real_escape_string,但是,您不应再使用mysql_*,而是使用PDO或mysqli。

有关详细信息,请参阅:How can I prevent SQL injection in PHP?

答案 4 :(得分:2)

是的,可以使用Unicode字符和操纵字符集来解决这个问题。

有关详细信息,请参阅https://security.stackexchange.com/questions/11391/how-did-anonymous-use-utf-16-ascii-to-fool-php-escaping