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



我们受到了攻击;黑客从下面显示的代码中页面进入系统,但我们无法找出此代码中的实际问题。

您能否指出此代码中的问题以及可能的解决方法?

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

这里的问题出在$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的情况下,如果我们将其键入转换为int,它将给出0((int)'Second_part_of_hash'结果为0)
所以最终0=0

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

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


要检查的好资源:

如何防止 PHP 中的 SQL 注入?

哈希可以阻止SQL注入吗?

补充零酷的优秀答案。

这里的问题是mysql(i)_real_escape_string阻止SQL注入的错误观念。不幸的是,太多人被引导相信此功能的目的是保护他们免受注射。当然,这几乎不是真的。

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

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

而且根本不会有任何注射。

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

(补充有关使用 PDO、正确使用密码等的其他答案/评论;在此处记录此内容,以防其他人偶然发现此问题。

没有人指出:

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

作为一个弱点。

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

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

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

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

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

$pass = $_POST["密码"];                实际密码  $pass = mysql_real_escape_string($pass);   实际密码的转义版本  $pass = hash("sha1",$pass, true);          转义密码的二进制哈希  此时,$pass是存储在数据库中的确切字符串。  $pass = mysql_real_escape_string($pass);   添加此行***  $query ="选择用户,从用户传递,其中用户='$user'和传递='$pass'";

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

相关内容

  • 没有找到相关文章

最新更新