做htmlspecialchars和mysql_real_escape_string让我PHP码安全的注射?
-
02-07-2019 - |
题
今天早些时候的一个问题是,要求有关 输入确认的战略在网络应用程序.
顶部回答,在编写本报告时,建议在 PHP
只是使用 htmlspecialchars
和 mysql_real_escape_string
.
我的问题是:这总够了吗?是否有更多我们应该知道吗?在哪里做这些职能打破?
解决方案
当涉及到数据库查询,总是试图和使用准备好的参数化的询问。的 mysqli
和 PDO
图书馆提供支持。这是无限的比较安全的使用功能,例如逃避 mysql_real_escape_string
.
是的, mysql_real_escape_string
是有效的,只是一串逃脱的功能。它不是一个魔术子弹。所有这将不是逃避危险的人物,以便他们可以安全使用在一个单一的查询串。然而,如果不消毒你输入之前,然后你将会受到某些攻击向量。
想象一下下面的SQL:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
你应该可以看到,这是易于受到利用。
想象一下 id
参数包含共同的攻击向量:
1 OR 1=1
有没有危险的字符,在那里进行编码,因此,它将直接通过逃跑的过滤器。我们留下:
SELECT fields FROM table WHERE id= 1 OR 1=1
这是一个可爱的SQL注射向量,并将允许攻击者返回的所有行。或
1 or is_admin=1 order by id limit 1
其产生的
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
它允许攻击者返回的第一管理的详细信息,这完全虚构的例子。
虽然这些功能是有用的,他们必须小心使用。你需要确保所有网络的投入进行验证。在这种情况下,我们看到,我们可以利用的,因为我们没有检查这一变我们作为一个数字,是实际数字。在PHP你应该广泛使用的一套功能检查的投入是整数,漂浮的字母数字等等。但是,当涉及到SQL,听取最大价值的准备好的发言。上述代码会有安全,如果它是一个准备的发言作为数据库的功能会知道 1 OR 1=1
是不是一个有效的文本。
作为对 htmlspecialchars()
.这是一个雷区。
有一个真正的问题在PHP在于,它有一个整体选择的不同html有关的逃离的功能,并且没有明确的指导意见究竟其功能做什么。
首先,如果你是内部的一个HTML标记,你是真正的麻烦。看看
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
我们已经在里面HTML标签,这样我们不需要 < 或做任何危险。我们的攻击向量可能仅仅是 javascript:alert(document.cookie)
现在所得HTML看起来像
<img src= "javascript:alert(document.cookie)" />
攻击得到直接通过。
它变得更糟。为什么?因 htmlspecialchars
(当时称为这种方式)只编码双报价并不是单一的。因此,如果我们了
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
我们的邪恶攻击者现在可以注入全新的参数
pic.png' onclick='location.href=xxx' onmouseover='...
给了我们
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
在这些情况下,没有神奇的子弹,你只需要santise输入自己。如果你尝试和筛选出恶人物你一定会失败。拿一个白名单办法,并仅让我们通过的文字,它是好的。看看 XSS作弊表 例如何多元化的载体可以
甚至如果你使用 htmlspecialchars($string)
外HTML标记,你仍然容易受到多字节charset的攻击向量。
最有效的,你可以是利用一个组合mb_convert_encoding和htmlentities如下。
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
即使这使IE6脆弱的,因为它处理UTF。但是,你可能会下降回到一个更有限的编码,如ISO-8859-1,直到IE6使用率下降。
为了更深入的研究的多问题,请参阅 https://stackoverflow.com/a/12118602/1820
其他提示
此外,Cheekysoft的极好的回答:
- 是的,他们会保护你的安全,但仅仅如果它们的使用绝对正确。使用他们不正确,你将仍然是脆弱的,并且可能有其它问题(例如数据的腐败)
- 请使用的参数化查询,而不是(正如上文)。你可以用它们通过例如公设辩护或通过一个包装的像梨DB
- 确保magic_quotes_gpc和magic_quotes_runtime是在所有时候,从来没有获得不小心打开,甚至没有简要介绍。这些都是早期和深入错误的尝试通过PHP的开发,以防止安全问题(这破坏了数据)
确实没有银弹防止HTML注射(例如跨站脚本),但是你可能能够实现它更加容易,如果你使用的图书馆或模板系统,用于输出HTML。阅读该文件,对于如何逃生的事情适当地进行。
在HTML,事情需要逃脱不同的上下文。这是真实的,尤其是串被放置到Javascript。
我肯定会同意上述帖子,但我有一个小问题可以回答Cheekysoft的回答,具体来说:
说到数据库查询, 总是尝试使用准备好的 参数化查询。 mysqli和 PDO库支持这一点。这是 比使用转义更安全 功能如 mysql_real_escape_string。
是的,mysql_real_escape_string是 实际上只是一个字符串转义 功能。它不是一个神奇的子弹。 它所能做的只是逃避危险 字符,以便它们可以 可以安全地在单个查询字符串中使用。 但是,如果你没有消毒你的 事先输入,然后你会 容易受到某些攻击媒介的影响。
想象一下以下SQL:
$ result = <!> quot; SELECT字段FROM表 WHERE id = <!> QUOT; .mysql_real_escape_string($ _ POST [ 'ID']);
你应该能够看到这是 容易被剥削。想象一下id 参数包含常见攻击 矢量:
1 OR 1 = 1
那里没有冒险的人物 编码,所以它将直接通过 通过逃逸过滤器。离开 我们:
SELECT字段FROM表WHERE id = 1 或1 = 1
我编写了一个快速的小函数,我放在我的数据库类中,它将删除任何不是数字的东西。它使用了preg_replace,所以有一些问题可能会更加优化,但是它可以用来捏......
function Numbers($input) {
$input = preg_replace("/[^0-9]/","", $input);
if($input == '') $input = 0;
return $input;
}
所以不要使用
$ result = <!> quot; SELECT字段FROM表WHERE id = <!> quot; .mysqlrealescapestring(<!> quot; 1 OR 1 = 1 <!> quot;);
我会用
$ result = <!> quot; SELECT字段FROM表WHERE id = <!> quot; .Numbers(<!> quot; 1 OR 1 = 1 <!> quot;);
它可以安全地运行查询
SELECT字段FROM表WHERE id = 111
当然,这只是阻止它显示正确的行,但我不认为对于试图将sql注入您的网站的人来说这是一个大问题;)
这个难题的一个重要部分是背景。发送<!>“1 OR 1 = 1 <!>”的人因为如果引用查询中的每个参数,ID都不是问题:
SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"
结果是:
SELECT fields FROM table WHERE id='1 OR 1=1'
无效。由于您正在转义字符串,因此输入不会突破字符串上下文。我已经对MySQL的5.0.45版本进行了测试,并且对整数列使用字符串上下文不会导致任何问题。
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
在64位系统上运行良好,甚至更好。请注意您在处理大数字时遇到的系统限制,但对于数据库ID,99%的情况下这很有用。
您也应该使用单一功能/方法来清理您的值。即使这个函数只是mysql_real_escape_string()的包装器。为什么?因为有一天会发现您首选的清理数据方法,只需将其更新到一个地方,而不是在系统范围内查找和替换。
为什么,为什么,你不在sql语句中包含用户输入的引号?似乎很傻到不!在sql语句中包含引号会呈现<!> quot; 1或1 = 1 <!> quot;没有成功的尝试,不是吗?
现在,你会说,<!>“如果用户在输入中包含引号(或双引号)怎么办?<!>
嗯,很容易解决这个问题:只需删除用户输入的引号即可。例如:input =~ s/'//g;
。现在,无论如何,在我看来,用户输入将是安全的......