【问题标题】:Are mysql_real_escape_string() and mysql_escape_string() sufficient for app security?mysql_real_escape_string() 和 mysql_escape_string() 是否足以保证应用安全?
【发布时间】:2011-07-21 20:18:56
【问题描述】:

mysql_real_rescape_string() 是否足以保护我免受黑客和 SQL 攻击?问是因为我听说这些对所有攻击媒介都没有帮助?寻求专家的建议。

编辑:另外,LIKE SQL 攻击呢?

【问题讨论】:

标签: php mysql security


【解决方案1】:

@Charles 非常正确!

您将自己置于多种已知 SQL 攻击的风险之中,包括您提到的

  • SQL 注入:是的! Mysql_Escape_String 可能仍然让您容易受到 SQL 注入的影响,具体取决于您在查询中使用 PHP 变量的位置。

考虑一下:

$sql = "SELECT number FROM PhoneNumbers " .
       "WHERE " . mysql_real_escape_string($field) . " = " . mysql_real_escape_string($value);  

这样可以安全准确地逃脱吗?不!为什么?因为黑客仍然可以这样做:

跟着我重复:

mysql_real_escape_string() 仅用于转义变量数据,NOT 表名、列名,尤其是 LIMIT 字段。

  • LIKE 漏洞利用:LIKE "$data%" 其中 $data 可能是 "%",这将返回所有记录...很可能是安全漏洞...只是想象一下通过信用卡的最后四位数字进行查找...哎呀!现在,黑客可能会收到您系统中的每个信用卡号! (顺便说一句:几乎不建议存储完整的信用卡!)

  • Charset Exploits:不管讨厌的人怎么说,Internet Explorer 仍然,在 2011 年,容易受到 Character Set Exploits 的攻击,而这如果你已经设计好了您的 HTML 页面正确,相当于 <meta name="charset" value="UTF-8"/>!这些攻击非常讨厌,因为它们给了黑客与直接 SQL 注入一样多的控制权:例如满的。

这里有一些示例代码来演示所有这些:

// Contains class DBConfig; database information.
require_once('../.dbcreds');                       

$dblink = mysql_connect(DBConfig::$host, DBConfig::$user, DBConfig::$pass);
mysql_select_db(DBConfig::$db);
//print_r($argv);

$sql = sprintf("SELECT url FROM GrabbedURLs WHERE %s LIKE '%s%%' LIMIT %s",
               mysql_real_escape_string($argv[1]),
               mysql_real_escape_string($argv[2]),
               mysql_real_escape_string($argv[3]));
echo "SQL: $sql\n";
$qq = mysql_query($sql);
while (($data = mysql_fetch_array($qq)))
{
        print_r($data);
}

这是传递各种输入时这段代码的结果:

$ php sql_exploits.php url http://www.reddit.com id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://www.reddit.com%'
               ORDER BY id;
Returns: Just URLs beginning w/ "http://www.reddit.com"

$ php sql_exploits.php url % id
SQL generated: SELECT url FROM GrabbedURLs 
               WHERE url LIKE '%%' 
               ORDER BY id;
Results: Returns every result Not what you programmed, ergo an exploit --

$ php sql_exploits.php 1=1 'http://www.reddit.com' id 结果: 返回每一列和每一个结果。

然后是真正令人讨厌的 LIMIT 漏洞利用:

$ php sql_exploits.php url 
> 'http://www.reddit.com'
> "UNION SELECT name FROM CachedDomains"
Generated SQL: SELECT url FROM GrabbedURLs 
               WHERE url LIKE 'http://reddit.com%' 
               LIMIT 1 
               UNION
               SELECT name FROM CachedDomains;
Returns:  An entirely unexpected, potentially (probably) unauthorized query
          from another, completely different table. 

你是否理解攻击中的 SQL 无关紧要。这表明 mysql_real_escape_string() 可以轻松被最不成熟的黑客绕过。那是因为它是一种反应性防御机制。它只修复了数据库中非常有限且已知的漏洞。

所有转义永远不足以保护数据库。事实上,您可以明确地对每一个已知的漏洞做出反应,并且在未来,您的代码很可能会受到未来发现的攻击。

正确且唯一(真正)的防御是一种主动防御:使用准备好的语句。准备好的语句在设计时特别小心,以便只执行有效和已编程的 SQL。这意味着,如果操作正确,能够执行意外 SQL 的几率会大大降低。

理论上,完美实现的准备好的语句将不受所有已知和未知的攻击,因为它们是一种服务器端技术,由数据库服务器自己和与编程语言接口的库处理。因此,您始终可以得到保护,至少免受所有已知的黑客攻击。

而且代码更少:

$pdo = new PDO($dsn);

$column = 'url';
$value = 'http://www.stackoverflow.com/';
$limit = 1;

$validColumns = array('url', 'last_fetched');

// Make sure to validate whether $column is a valid search parameter.
// Default to 'id' if it's an invalid column.
if (!in_array($column, $validColumns) { $column = 'id'; }


$statement = $pdo->prepare('SELECT url FROM GrabbedURLs ' .
                           'WHERE ' . $column . '=? ' .
                           'LIMIT ' . intval($limit));
$statement->execute(array($value));
while (($data = $statement->fetch())) { }

现在这不是那么难,是吗?而且它代码减少了 47%(195 个字符 (PDO) 与 375 个字符 (mysql_)。这就是我所说的“充满胜利”。

编辑:为了解决这个答案引发的所有争议,请允许我重申我已经说过的话:

使用准备好的语句可以利用以下保护措施 SQL 服务器本身,因此 您受到保护,不受 SQL Server 人们都知道。因为 这种额外的保护,你 比仅仅使用安全得多 逃避,无论多么彻底。

【讨论】:

  • 你准备好的语句示例简直太荒谬了:)你碰巧运行它吗?
  • LIKE 表达式的数据应另外通过转义 %_ 进行清理。
  • @Theodore R. Smith:“所有转义永远都不足以保护数据库。” --- 嗯,你能证明吗?没有太多的理论和大声的话语。我创建了一个使用 mysql_real_escape_string 的脚本,并且您正在使用任何类型的 sql 注入来获取受保护的数据。好的?你有足够的能力去做一些真正的事情,而不仅仅是哲学吗?
  • 真可惜。你不明白这一点。这不是“编码器的意思”。它是“语言操作员的意思”。它的运算符意味着从整个表中返回多个结果,因此,编码器必须期望它。如果只需要一个匹配项,则应使用 NO LIKE。就这样。和 mysql_real_escape_string() 不会帮助您处理 VARIABLE 但 INT 类型。你拒绝进一步学习,这让我很难过。这可能是因为您仍然在做大量的“研究”,并且为自己感到骄傲而无法倾听。
  • 并且您以错误的方式更正了限制。 PDO 可以使用限制参数,您可以(并且应该)为此使用准备好的语句。事实上,你的查询组装代码看起来并不比普通的 mysql 更好:)
【解决方案2】:

不!


重要更新:在测试possible exploit code provided by Col. Shrapnel并查看MySQL 5.0.22、5.0.45、5.0.77和5.1.48版本后,似乎GBK字符集 以及其他可能与低于 5.0.77 的 MySQL 版本结合使用,如果您使用SET NAMES 而不是使用特定的mysql_set_charset,则可能会使您的代码易受攻击/mysqli_set_charset 函数。因为这些只是在 PHP 5.2.x 中添加的,旧 PHP 和旧 MySQL 的组合可能会产生潜在的 SQL 注入漏洞,即使您认为自己是安全的并且做的一切都是正确的,但是 -书。


如果设置字符集mysql_real_escape_string 结合使用,您可能会发现自己容易受到旧 MySQL 版本可能存在的特定字符集攻击。 More info on previous research

如果可能,请使用mysql_set_charset。如果您使用的是受影响的 MySQL 版本(5.0.22 5.0.77 之前),SET NAMES ... 不足以防止此特定漏洞利用。

【讨论】:

  • +1 这是一个准确的答案,并列出了一个额外的漏洞以及如何修复它。请参阅下面的详细回答,了解所有当前已知的 SQL 漏洞,mysql_real_escape_string 对每个漏洞的不足,以及安全且简单的替代方案:PDO 准备语句。
  • -1 没有提到只有在使用极其罕见的编码时才会发生所有这些废话,但对 utf 和单字节编码没有害处。
  • @Col,帖子中链接了对极其罕见问题的解释,我确实详细说明了这是一个与旧版本 MySQL 相关联的非常具体的漏洞。
  • 大声笑,你甚至没有明白这一点。 SET NAMES 对于任何 mysql 版本仍然不够
  • @Col,请提供一个查询和用户输入,当使用SET NAMES 后通过mysql_real_escape_string 时,这表明MySQL 版本更新 比5.0.22 中的漏洞.或者,换一种说法,坚持或闭嘴。你一直在责备人们“不理解”和“错过重点”,但从来没有真正解释过你所知道的,而其他人似乎没有这样做。这是你的机会。
【解决方案3】:

是的。如果你不会忘记:

  1. 使用mysql_real_rescape_string() 转义字符串数据
  2. 将数字显式转换为数字(即:$id = (int)$_GET['id'];

那么你就受到保护了。

【讨论】:

  • +1 如果你忘记了 2 并使用 mysql_real_escape_string(),请始终引用它们 :)
  • @alex 有时你不能完全理解他们
  • -1 这是非常危险的坏建议。我并不是要刻薄——你可能不知道更好——但是 SQL 安全性不仅仅是转义输入变量……还有一个更简单、更安全的系统,我将在下面(详细)描述.
  • @Theodore R. Smith:嗯,在我的实践中,我从不从用户输入中获取字段或表名。是的,查询可以依赖于输入,但我从来没有把除了数据之外的任何东西放到 sql 中。所以,我认为我的建议仍然是好的和性感的。
  • @Col。弹片:不知道为什么要按原样从请求中获取标识符。从来没有这样的任务。
【解决方案4】:

我个人更喜欢prepared statements

<?php
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
  while ($row = $stmt->fetch()) {
    print_r($row);
  }
}
?>

在使用 *escape_string() 函数之一时很容易忽略一个或另一个特定变量,但如果您的所有查询都是准备好的语句,那么它们都很好,并且使用插值变量将像拇指酸痛一样脱颖而出。

但这远远不足以确保您不会受到远程攻击:如果您通过 &amp;admin=1GETPOST 请求表明某人是管理员,则每个您的用户只需两三秒即可轻松升级他们的权限。请注意,这个问题并不总是那么明显 :) 但这是解释过度信任用户提供的输入的后果的一种简单方法。

【讨论】:

    【解决方案5】:

    您应该考虑改用准备好的语句/参数化查询。这个想法是你给数据库一个带有占位符的查询。然后你给数据库你的数据,并告诉它用所述数据替换哪个占位符,并且数据库确保它是有效的并且不允许它超出占位符(即它不能结束当前查询然后添加它自己的 - 一种常见的攻击)。

    【讨论】:

    • +1 这是一个准确的答案,并列出了如何防止 SQL 漏洞利用的主要方法。有关所有当前已知的 SQL 漏洞,请参阅下面我的详细答案,mysql_real_escape_string 对每个漏洞的不足,以及@AgentConundrum 的安全且简单的替代方案:PDO 准备语句。
    猜你喜欢
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 2011-10-16
    • 2015-03-08
    • 1970-01-01
    • 2013-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多