【发布时间】:2011-07-21 20:18:56
【问题描述】:
mysql_real_rescape_string() 是否足以保护我免受黑客和 SQL 攻击?问是因为我听说这些对所有攻击媒介都没有帮助?寻求专家的建议。
编辑:另外,LIKE SQL 攻击呢?
【问题讨论】:
-
LIKE 攻击没什么大不了的。除非使用不当,否则 LIKE 不会造成任何伤害。只是不要使用 LIKE 代替
=就可以了。
mysql_real_rescape_string() 是否足以保护我免受黑客和 SQL 攻击?问是因为我听说这些对所有攻击媒介都没有帮助?寻求专家的建议。
编辑:另外,LIKE SQL 攻击呢?
【问题讨论】:
= 就可以了。
@Charles 非常正确!
您将自己置于多种已知 SQL 攻击的风险之中,包括您提到的
考虑一下:
$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 表达式的数据应另外通过转义 % 和 _ 进行清理。
重要更新:在测试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 ... 不足以防止此特定漏洞利用。
【讨论】:
mysql_real_escape_string 对每个漏洞的不足,以及安全且简单的替代方案:PDO 准备语句。
SET NAMES 后通过mysql_real_escape_string 时,这表明MySQL 版本更新 比5.0.22 中的漏洞.或者,换一种说法,坚持或闭嘴。你一直在责备人们“不理解”和“错过重点”,但从来没有真正解释过你所知道的,而其他人似乎没有这样做。这是你的机会。
是的。如果你不会忘记:
mysql_real_rescape_string() 转义字符串数据
$id = (int)$_GET['id'];)那么你就受到保护了。
【讨论】:
mysql_real_escape_string(),请始终引用它们 :)
我个人更喜欢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() 函数之一时很容易忽略一个或另一个特定变量,但如果您的所有查询都是准备好的语句,那么它们都很好,并且使用插值变量将像拇指酸痛一样脱颖而出。
但这远远不足以确保您不会受到远程攻击:如果您通过 &admin=1 和 GET 或 POST 请求表明某人是管理员,则每个您的用户只需两三秒即可轻松升级他们的权限。请注意,这个问题并不总是那么明显 :) 但这是解释过度信任用户提供的输入的后果的一种简单方法。
【讨论】:
您应该考虑改用准备好的语句/参数化查询。这个想法是你给数据库一个带有占位符的查询。然后你给数据库你的数据,并告诉它用所述数据替换哪个占位符,并且数据库确保它是有效的并且不允许它超出占位符(即它不能结束当前查询然后添加它自己的 - 一种常见的攻击)。
【讨论】:
mysql_real_escape_string 对每个漏洞的不足,以及@AgentConundrum 的安全且简单的替代方案:PDO 准备语句。