sql注入易受攻击的代码,即使我们正在清理输入的mysql_real_escape_string

时间:2019-04-04 18:41:40

标签: php sql security sql-injection owasp

我们在哪里遭到攻击, 那些黑客从下面显示代码的页面进入系统,但是我们无法找出此代码中的实际问题。 您能否指出这段代码中的问题以及可能的解决方法

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

?>

5 个答案:

答案 0 :(得分:4)

这里的问题出在$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'将导致0,并且0='Second_part_of_hash'由SQL引擎进行类型转换,但是如果是string,则我们将其强制转换为{{1 }}的值为0(int的结果为(int)'Second_part_of_hash'
所以最后0

0=0

每次都会产生“ true”,并且您可以看到它可以应用于所有哈希函数,例如MD5和sha256等。


要检查的好资源:

How can I prevent SQL injection in PHP?

Could hashing prevent SQL injection?

答案 1 :(得分:1)

以补充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 statement 。特别是考虑到 mysql扩展在PHP中已不存在,而所有其他扩展都支持预备语句的事实(但是,如果您想减轻过渡的痛苦,则应该使用PDO,尽管这很矛盾)可能会听起来)。

答案 2 :(得分:0)

(关于使用PDO,正确使用密码等其他答案/评论的补充;在其他人偶然发现此问题的情况下,请在此处登录。)

没有人指出:

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

是弱点。

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

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

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

答案 3 :(得分:-1)

您可以重写验证逻辑,以快速解决@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_ *函数,并使用更强大的哈希算法。

答案 4 :(得分:-1)

您可以通过添加一行来修正现有代码 而不会破坏任何现有密码

 $pass = $_POST['password'];                 // the actual password
 $pass = mysql_real_escape_string($pass);    // escaped version of the actual password
 $pass = hash("sha1",$pass, true);           // binary hash of the escaped password
 // At this point, $pass is the exact string that is stored in the database.
 $pass = mysql_real_escape_string($pass);    // ***ADD THIS LINE***
 $query = "select user, pass from users where user='$user' and pass='$pass'";

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