即使我们正在清理输入 mysql_real_escape_string 的 SQL 注入漏洞代码

Posted

技术标签:

【中文标题】即使我们正在清理输入 mysql_real_escape_string 的 SQL 注入漏洞代码【英文标题】:SQL injection vulnerable code even when we are sanitizing the input mysql_real_escape_string 【发布时间】:2019-08-26 14:56:47 【问题描述】:

我们被攻击了;黑客从下面代码中的 页面进入系统,但我们无法找出这段代码中的实际问题。

您能否指出这段代码中的问题以及可能的修复方法?

    <?php
        //login.php page code
        //...
        $user = $_POST['user'];
        $pass = $_POST['password'];
        //...
        mysql_connect("127.0.0.1", "root", "");
        mysql_select_db("xxxx");

        $user = mysql_real_escape_string($user);
        $pass = mysql_real_escape_string($pass);
        $pass = hash("sha1", $pass, true);
        //...
        $query = "select user, pass from users where user='$user' and pass='$pass'";
        //...

    ?>

【问题讨论】:

你不应该使用 SHA1 password hashesMD5 password hashes 而你真的应该使用 PHP 的built-in functions 来处理密码安全。在散列之前,请确保您 don't escape passwords 或对它们使用任何其他清理机制。这样做会更改密码并导致不必要的额外编码。 请stop using mysql_* functions。 These extensions 已在 PHP 7 中删除。了解 PDO 和 @987654329 的 prepared 语句@ 并考虑使用 PDO,it's really pretty easy。 这已经不好笑了。 Little Bobby 说 your script is at risk for SQL Injection Attacks.。即使escaping the string 也不安全! 真正的问题是这段代码的all。错误的密码散列、过时的 MySQL 函数、转义字符串、SQL 注入漏洞。这个制作多久了? 您如何确定这是 SQL 注入而不是暴力破解?还请显示代码的查询执行部分,它决定登录成功或无效。 【参考方案1】:

这里的问题出在$pass= hash("sha1",$pass, true);

你需要这样写$pass= hash("sha1",$pass, false);

一个不错的选择是迁移到 PDO。


让我们看看为什么会这样:

您的代码正在做的是返回一个原始二进制哈希,这意味着在某个时间点该哈希可能包含相等的字符=, 对于您的示例,在这种情况下将导致 SQL 注入的哈希是 "ocpe",因为哈希 ("ocpe",sha1) 有一个 '=' 字符, 但是我怎么能弄明白呢?

您只需要运行一个简单的暴力破解并测试它是否在哈希原始位中包含'='

这是一个可以帮助你的简单代码

<?php
$v = 'a';
while(1)

        $hash = hash("sha1",$v, true);
        if( substr_count( $hash, "'='" ) == 1 ) 
            echo $v;
            break;
        
        $v++;


?>

现在你有一个字符串,它给出了一个内部相等的哈希'='

查询变为:

$query = "select user, pass from users where user='$user' and pass='hash("ocpe",sha1)'";

然后

$query = "select user, pass from users where user='$user' and pass='first_Part_of_hash'='Second_part_of_hash'";

在这种情况下,我假设 ocpe 字符串具有这种格式的哈希 first_Part_of_hash'='Second_part_of_hash

因为pass='first_Part_of_hash' 将导致00='Second_part_of_hash' 由SQL 引擎进行类型转换,但如果我们将string 类型转换为int,它将给出0 (@ 987654340@ 是0) 所以最后0=0

$query = "select user, pass from users where user='$user' and 0=0";

每次都会产生“真”,如您所见,它可以应用于所有哈希函数,如 MD5 和 sha256 等。


好资源检查:

How can I prevent SQL injection in PHP?

Could hashing prevent SQL injection?

【讨论】:

很好的解释。 你应该使用 PHP 的内置密码函数并同时避免使用 SHA1 @JayBlanchard 是的,但他想要一个解释,我也为他参考了 SO 中关于“防止 PHP 中的 SQL 注入?”的最佳文章。 @JayBlanchard,确实应该使用password_hash(),但告诉某人更改他们当前的哈希方法并不简单。您如何建议他们将当前用户的哈希密码转换为新方法? 总结起来很简单:你不能直接转换,因为散列是单向的。在修改您的应用程序以使用较新的哈希格式存储密码后,您必须要求您的用户重新输入他们的密码。应用需要使用任一密码对用户进行身份验证,直到所有用户都更新了密码。【参考方案2】:

补充zerocool的出色答案。

这里的问题是false notion that mysql(i)_real_escape_string prevents SQL injection。不幸的是,太多的人被引导相信这个功能的目的是保护他们免受注射。当然,这几乎不是真的。

如果此代码的作者正确理解此函数的目的(即转义字符串文字中的特殊字符),他们会将此代码编写为

$user = mysql_real_escape_string($user);
$pass = hash("sha1", $pass, true);
$pass = mysql_real_escape_string($pass);

根本不会有任何注射。

这里我们得出一个重要的结论:鉴于转义的目的不是防止 SQL 注入,为此我们应该使用另一种机制,即prepared statements。特别是考虑到 mysql 扩展不再存在于 PHP 中,而所有其他扩展都支持准备好的语句(但是,如果你想减少转换的痛苦,你绝对应该使用 PDO,但它是自相矛盾的)可能听起来)。

【讨论】:

如果你想看看我上面提到的其他技巧,只需谷歌“exploit -db”并开始查看“SQL 注入”POC,如果你有兴趣,你会发现很多这样的技巧【参考方案3】:

(关于使用 PDO、正确使用密码等的其他答案/cmets 的补充;在此处记录以防其他人偶然发现此问题。)

没有人指出:

mysql_connect("127.0.0.1","root","");
mysql_select_db("xxxx");

作为一个弱点。

这意味着: - DB 服务器与 Web 服务器位于同一主机上,因此具有与世界连接的网络接口。 - 这有最基本的用户(root)可用, - 并且没有密码。

希望这是一个示例/测试,但如果不是,请确保至少服务器端口 (3306) 被防火墙阻止/无法从外部访问。

否则一个简单的mysql -h [webserver address] -u root 将连接,游戏就结束了。

【讨论】:

【参考方案4】:

您可以重写验证逻辑,以快速解决@zerocool 解释的问题。

// don't send password hash to mysql, user should be uniqe anyway
$query = "select user, pass from users where user='$user'";

// validate hash in php
if (hash_equals(hash('sha1', $pass, true), $user_hash_from_db))...

正如其他人所写,尽快停止使用 mysql_* 函数,并使用更强大的哈希算法。

【讨论】:

【参考方案5】:

您可以通过添加一行在不破坏任何现有密码的情况下修复现有代码

$pass = $_POST['密码']; // 实际密码 $pass = mysql_real_escape_string($pass); // 实际密码的转义版本 $pass = hash("sha1",$pass, true); // 转义密码的二进制哈希 // 此时,$pass 是存储在数据库中的确切字符串。 $pass = mysql_real_escape_string($pass); // ***添加此行*** $query = "select user, pass from users where user='$user' and pass='$pass'";

请注意,存储在数据库中的密码是实际密码的转义版本的二进制哈希。由于它是二进制字符串,因此您需要对其进行转义。 一定要先在存储密码的代码中添加额外的转义,否则密码设置也会有SQL注入漏洞。

【讨论】:

以上是关于即使我们正在清理输入 mysql_real_escape_string 的 SQL 注入漏洞代码的主要内容,如果未能解决你的问题,请参考以下文章

WordPress恶意软件 - 重定向到(fast.destinyfernandi.com) - 即使在扫描和清理之后[关闭]

清理来自 irc 的输入

清理用户输入

使用用户插入的变量清理 exec 命令的最佳方法

如何清理 sql.js 输入?

如何清理dns缓存