strip_tags()函数是否容易受到脚本攻击的影响?

57

是否已知有一种XSS或其他攻击可以通过a标签

$content = "some HTML code";
$content = strip_tags($content);

echo $content;

?

手册中有一条警告:

此函数不会修改您允许使用 allowable_tags 的标签的任何属性,包括样式和 onmouseover 属性,这些属性可能会被恶意用户滥用,当发布将显示给其他用户的文本时。

但是这只与仅使用 allowable_tags 参数有关。

如果没有设置允许的标签strip_tags() 是否容易受到攻击?

Chris Shiflett 似乎认为它很安全:

使用成熟的解决方案

在可能的情况下,使用成熟的、现有的解决方案,而不是尝试创建自己的解决方案。像 strip_tags() 和 htmlentities() 这样的函数是不错的选择。

这正确吗?如果可以,请引用来源。

我知道有 HTML purifier、htmlspecialchars() 等等 - 我不是在寻找最佳的 HTML 消毒方法。我只想了解这个具体问题。这是一个理论性的问题,在这里提出。

参考:在 PHP 源代码中实现 strip_tags() 函数


1
没有给出任何参数,它将删除 所有 标签,所以我不知道会有任何漏洞。最糟糕的情况是有人给你提供无效的标记(没有关闭标记),但这种最坏情况只意味着 strip_tags 将会删除更多的文本。 - Damon
@Gordon 谢谢,但我的意思是整个HTML数据块,不包括文件名等注入。(如果我理解正确的话,这就是该论坛条目正在讨论的内容。)就我所看到的,该线程并没有证明strip_tags()存在漏洞,而是建议使用htmlspecialchars()——这也是我通常做的,但我想知道是否真的必要 - Pekka
4
http://htmlpurifier.org/comparison#striptags 的态度有点不屑一顾。这可能是显而易见的,但对新手需要再次强调:striptags 是否安全取决于上下文。如果输出最终在属性中使用,则不安全。只有当剥离的内容进入页面主体时才可以(对此确实足够)。 - mario
1
@Pekka http://packetstormsecurity.org/search/?q=strip_tags@Pekka http://packetstormsecurity.org/search/?q=strip_tags - Gordon
1
strip_tags() 实现的更新链接:https://github.com/php/php-src/blob/master/ext/standard/string.c#L4729 - Sumit
显示剩余6条评论
6个回答

54
作为其名称可能暗示的那样,strip_tags 应该删除所有 HTML 标签。我们可以通过分析源代码来证明它。下面的分析适用于一个没有第二个参数进行白名单标签的 strip_tags('...') 调用。
首先,关于 HTML 标签的一些理论:标签以 < 开头,后跟非空格字符。如果这个字符串以 ? 开头,则 不应该被解析。如果这个字符串以 !-- 开头,则被视为注释,接下来的文本也不应该被解析。注释以 --> 结束,在这样的注释中,允许使用像 <> 这样的字符。属性可以出现在标签中,它们的值可以选择由引号字符 ('") 包围。如果存在这样的引号,它必须被关闭,否则如果遇到 >,则标签未关闭。
代码 <a href="example>xxx</a><a href="second">text</a> 在 Firefox 中被解释为:
<a href="http://example.com%3Exxx%3C/a%3E%3Ca%20href=" second"="">text</a>

PHP函数strip_tagsext/standard/string.c的第4036行中被引用。该函数调用内部函数php_strip_tags_ex

有两个缓冲区,一个用于输出,另一个用于“HTML标记内部”。一个名为depth的计数器保存开放角括号(<)的数量。
变量in_q包含引号字符('"),如果没有则为0。最后一个字符存储在变量lc中。

该函数有五种状态,其中三种在函数上面的描述中提到。根据这些信息和函数体,可以得出以下状态:

  • 状态0是输出状态(不在任何标记中)
  • 状态1表示我们在普通HTML标记内部(标记缓冲区包含<
  • 状态2表示我们在PHP标记内部
  • 状态3:我们从输出状态进入,并遇到<!字符(标记缓冲区包含<!
  • 状态4:在HTML注释中
我们需要小心,确保没有标签被插入。也就是说,<后面不能跟非空格字符。第4326行检查了一个包含<字符的情况,如下所述:
  • 如果在引号内(例如<a href="inside quotes">),则忽略<字符(从输出中删除)。
  • 如果下一个字符是空格字符,则将<添加到输出缓冲区
  • 如果不在HTML标记中,则状态变为1(“在HTML标记内”),并将最后一个字符lc设置为<
  • 否则,如果在HTML标记内部,则增加名为depth的计数器,并忽略该字符。

如果在标记打开时遇到>state == 1),则in_q变为0(“不在引号内”),state变为0(“不在标记中”)。标记缓冲区将被丢弃。

对于标记缓冲区进行属性检查(例如'"等字符)。因此,结论是:

strip_tags没有标记白名单时,在标记外部包含是安全的,不会允许任何标记。

“外部标签”指的是不在标签中的文本,例如:<a href="in tag">outside tag</a>。文本可能包含<>,例如:>< a>>。但结果并不是有效的HTML,需要转义<>&,特别是&。可以使用htmlspecialchars()函数进行转义。

没有白名单参数的strip_tags函数的描述如下:

确保返回的字符串中不存在任何HTML标签。


16
所以……简而言之,是的,“strip_tags()”是安全的吗? - rinogo
@rinogo 是的,当只使用一个参数调用 strip_tags 时是安全的。 - bishop

11

我无法预测未来的攻击,尤其是因为我没有查看此PHP源代码。但是,过去曾经出现过由于浏览器接受看似无效的标签(如<s\0cript>)而导致的漏洞。所以未来有人可能会利用奇怪的浏览器行为进行攻击。

不过,将输出直接作为完整的HTML块发送到浏览器应该永远不会存在安全隐患:

echo '<div>'.strip_tags($foo).'</div>'

然而,这并不安全:

echo '<input value="'.strip_tags($foo).'" />';

因为一个人可以很容易地通过使用"结束引号并插入脚本处理程序。

我认为总是将零散的<转换为&lt;(以及引号也是如此)会更安全。


7
根据这个在线工具,这个字符串将被“完美”转义,但结果是另一个恶意的字符串!
<<a>script>alert('ciao');<</a>/script>

在这个字符串中,“真正的”标签是<a></a>,因为单独使用<script>不是标签。
我希望我是错误的,或者只是因为PHP版本过旧,但最好在您的环境中进行检查。

3
在PHP 7.1.2中,var_dump(strip_tags("<<a>script>alert('ciao');<</a>/script>"));的结果是"alert('ciao');"。它似乎会删除从<>之间的所有内容。 - luckydonald
哦,那很糟糕。不过在这里似乎通过了所有的测试:https://3v4l.org/BBapp#output - Darragh Enright
值得一提的是,在 PHP 8 中,这现在只会导致 alert('ciao'); 的结果。 - Antony
这里引用的工具使用JavaScript解析字符串,因此并不是问题的很好代表。 - Christiaan

3

是的strip_tags() 对脚本攻击是易受攻击的,至少到 PHP 8。不要使用它来防止 XSS。相反,您应该使用 filter_input()

strip_tags() 易受攻击的原因在于它不会递归运行。也就是说,在剥离有效标记后,它不会检查是否还有有效标记存在。例如,字符串
<<a>script>alert(XSS);<</a>/script> 成功剥离了 <a> 标记,但未能看到这样留下了
<script>alert(XSS);</script>

可以在这里(在安全环境下)看到示例。


但我猜如果经过“strip_tags”处理后用户输入发生变化,仍然可以使用它完全拒绝用户输入,对吧? - iSWORD
1
值得一提的是,在PHP 8中,这现在只会导致alert('XSS'); - Antony

1

如果你只是将文本输出到html主体中,那么去除标记是完全安全的。

但将其放入mysql或url属性中并不一定安全。


尽管这个答案已经有10年的历史了,但值得一提的是,如果你像我一样在2021年遇到了这个问题——这个答案是完全不正确的,甚至是危险的信息。 - Obsidian Age

-1

我刚刚通过在 href 中使用 strip_tags() 成功注入了 PHP 8 的脚本:

测试使用:

<a href="javascript:alert(1)">Click me!</a>

显然这需要用户交互,但是通过该函数传递。

类似于 strip_tags()是否容易受到脚本攻击? 但没有其他符号。


已经在此处提到:[https://dev59.com/hW025IYBdhLWcg3wzZT3#5793677] - Your Common Sense

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接