最简单的场景是尝试发送--><?php phpinfo();?><!--
. 如果<?php
标签被转义,那么这将导致
<!-- -->
<?php phpinfo();
<!-- -->
(为清楚起见添加了换行符)。但是<?php ...?>
在 HTML 页面中的存在可能还不够;PHP 代码需要在服务器端进行解释,而不仅仅是发送回客户端。
通过更仔细地检查结果字符串的开头:
<!--?
很明显,不是 PHP 标记,而是<
触发转义的唯一标记,这可能是为了作为 HTML/XML/XSS 防御。这意味着您无法发送活动内容并使其执行或以客户端可执行形式呈现。
您可以再次尝试“预先转义”,通过发送
< --><hr><!-- >
看看这是否会天真地转化为
<!----><hr><!-- -->
它将被呈现为 HTML,或者被更彻底地改造成
<!-- -- --><!-- hr --><!-- !-- -->
这没有任何用处。这完全取决于 HTML 打开标记检测是如何完成的,以及它是如何操作的。有时preg_replace
与不正确、不完整或不够贪婪的正则表达式一起使用,它可能只去除第一个或最后一个标签,或者盲目地用最后一个结束去除第一个开头,忽略中间的任何内容。如果是这样,那么该页面很容易受到 Javascript 注入和各种相关攻击。
消毒代码是否可供检查?
朴素验证示例
例如,这段代码显然净化了 HTML 输出。
preg_replace("/<(.*)>/", "<!-- \\1 -->", $input);
但是(除了是一个经过深思熟虑的措施之外)缺乏不贪婪的运营商?使其容易受到简单的预转义攻击:
<?php
$params = array(
"<script>alert('FAIL');</script>",
"< --><script>alert('Success');</script><!-- >",
);
foreach($params as $param)
print preg_replace("/<(.*)>/", "<!-- \\1 -->", $param) . "\n\n\n";
?>
导致第一次天真的攻击失败,而第二次成功:
<!-- script>alert('FAIL');</script -->
<!-- --><script>alert('Success');</script><!-- -->
不幸的是,这个preg_replace
选项经常被建议或实现为 HTML 注入攻击的“快速修复”,并且由于大多数“临时修复”不会这样做,它可能会成为永久性的。
更好的策略是过滤掉不属于原始参数的任何内容(例如[^A-Za-z0-9_]
,用任何内容替换),或者认为禁止字符的存在意味着以这种方式出现了一些邪恶的东西,因此最安全的反应是完全放弃请求(可能会通知用户,如果它偶然发生或由于问题而发生 - 例如,可能是语法不正确的链接 - 在其他地方;因此HTTP_REFERER
强烈建议记录)。