前言
一个很常见的现象 ,我们去搜索如何防御 XSS 攻击的时候,经常会看到一种防御方式就是使用htmlspecialchars
,印象里之前在初学阶段,面试官在问及我如何防御 XSS 漏洞的时候,我也最喜欢说使用htmlspcialchars
去过滤,那这种过滤方法真的安全吗?
今天在这篇文章里我们主要涉及到讨论 htmlspecialchars
的过滤效果及不同效果是否存在漏洞及配套的绕过手法。
Htmlspecialchars 函数介绍
htmlspecialchars
函数是 PHP 中的一个内置函数,它用于将特定的 HTML 字符转换为 HTML 实体字符。在 HTML 中,某些字符如 <
, >
, "
, '
, 和 &
具有特殊含义,分别代表HTML的标签、结束标签、字符引用和实体引用。如果想要在 HTML 中输出这些字符而不让它们被浏览器解释为 HTML 标签或字符引用,就需要使用 htmlspecialchars
函数将它们转换为相应的 HTML 实体。
函数的基本语法如下:
htmlspecialchars(string $string, int $quote_style = ENT_COMPAT, string $encoding = 'UTF-8', bool $double_encode = true)
参数说明:
$string
:需要转换的字符串。$quote_style
:指定引号的风格。通常有三个可选值:ENT_COMPAT
、ENT_QUOTES
和ENT_NOQUOTES
。ENT_COMPAT
会输出成<
、>
、"
、'
、&
,ENT_QUOTES
会在ENT_COMPAT
的基础上将单引号也转换为实体字符('
),ENT_NOQUOTES
则不会输出引号。$encoding
:设置字符编码,默认是UTF-8
。$double_encode
:布尔值,指示是否对已经存在的实体进行再次编码。
使用htmlspecialchars
函数可以避免跨站脚本攻击(XSS),因为它可以确保用户输入的字符串被正确地处理,不会因为浏览器解释特殊字符而执行不必要的脚本。
函数过滤效果示例
首先我们编写一段最基本的代码,用于接收GET
传送的参数comment
,其后端处理代码如下:
<?php
if ($_SERVER["REQUEST_METHOD"] == "GET") {
$comment =$_GET['comment'];
echo "<h1>您的评论:</h1>";
echo "<h2 align='center'>没有找到和" . htmlspecialchars($comment) . "相关的结果。</h2>";
}
?>
此时我们构造数据包,向comment
参数发送最简单的 XSS 攻击语句,可以从数据包中看到,htmlspecialchars
过滤了尖括号:
此时即使前端可以反应我们输入的数据,但是无法被当作 JS 代码执行:
请注意,这里我们使用的是默认的htmlspecialchars
,如果alert(1)
修改为alert("1")
,那么其中的双引号也会被转义。
如何绕过,场景是什么
可以看到过滤是不成功的,那么网上的各种绕过是怎么回事呢?
经过笔者的研究,发现大部分的htmlspecialchars
的绕过场景是这样的,后端代码如下:
<?php
if ($_SERVER["REQUEST_METHOD"] == "GET") {
$comment =$_GET['comment'];
// 对输入进行HTML实体编码
echo "<h1>您的评论:</h1>";
$comment=htmlspecialchars($comment);
// 这里增加了一个表单输入,以便用户可以输入关键词进行搜索
echo "<form action='' method='get'>";
echo "<input type='text' name='keyword' value='$comment'>";
echo "<input type='submit' value='搜索'>";
echo "</form>";
}
?>
此时我们再度去传送相同的数据包,会发现如下的返回数据包,在这个数据包中我们传入的内容在经过htmlspecialchars
函数处理后,被被赋予到<input>
块中的value
,在这个过程中,尖括号被转义导致无法正常执行想要执行的恶意javascript
代码,但是此时我们使用的是默认的htmlspcialchars
,在这种情况下,value
是使用单引号闭合的,那我们可以不可以通过单引号闭合去达到绕过的效果呢?
以上想法付诸实践,构造恶意 payload 如下:
http://xxx.COM/xss.php?comment=' onmouseover=javascript:alert(123) >
发送数据包后返回的数据包如下,可以看到闭合完成:
访问目标网页,触发 XSS 漏洞:
思考,如果 input
标签里闭合方式是双引号还可以完成吗?此时我们修改后端代码如下:
<?php
if ($_SERVER["REQUEST_METHOD"] == "GET") {
$comment =$_GET['comment'];
$comment=htmlspecialchars($comment);
// 对输入进行HTML实体编码
echo "<h1>您的评论:</h1>";
// 这里增加了一个表单输入,以便用户可以输入关键词进行搜索
echo "<form action='' method='get'>";
echo '<input type="text" name="keyword" value="$comment">';
echo "<input type='submit' value='搜索'>";
echo "</form>";
}
?>
可以看到返回的数据包中,双引号被转义,而前端也无法正常执行恶意的Javascript
代码,因此达到防御的效果:
思考,如何防御第一种使用单引号闭合的效果?
此时我们关注到这样一个参数,
$quote_style
:指定引号的风格。通常有三个可选值:ENT_COMPAT
、ENT_QUOTES
和ENT_NOQUOTES
。ENT_COMPAT
会输出成<
、>
、"
、'
、&
,ENT_QUOTES
会在ENT_COMPAT
的基础上将单引号也转换为实体字符('
),ENT_NOQUOTES
则不会输出引号。
也就是说,如果不设置这个参数,默认只过滤双引号而不过滤单引号,那么此时我们修改后端代码如下:
<?php
if ($_SERVER["REQUEST_METHOD"] == "GET") {
$comment =$_GET['comment'];
// 对输入进行HTML实体编码
echo "<h1>您的评论:</h1>";
$comment=htmlspecialchars($comment,int $quote_style = ENT_QUOTES);
// 这里增加了一个表单输入,以便用户可以输入关键词进行搜索
echo "<form action='' method='get'>";
echo "<input type='text' name='keyword' value='$comment'>";
echo "<input type='submit' value='搜索'>";
echo "</form>";
}
?>
此时再此发送恶意的单引号过滤方法,那么返回的数据包如下,可以看到之前的绕过方法已经不奏效了,无法达到绕过。
因此其实对htmlspecialchars
的理解就很简单了,这个函数默认过滤尖括号,井号等危险符号,同时过滤双引号,想要绕过的话,需要在有单引号参数赋值的情况下通过构造闭合去绕过,如果这个函数设置了额外的参数,比如连单引号也转义,那么就无法绕过。
总结
在本篇文章里,我们主要学习了htmlspcialchars
函数的过滤效果、默认的过滤和额外过滤效果的不同,以及特定情况下的绕过手法,XSS 博大精深,攻防之间需要从业人员更多的钻研和心血。