[红日安全]代码审计Day12 - 误用htmlentities函数引发的漏洞
字数 1482 2025-08-18 11:37:37
HTML实体编码误用引发的安全漏洞分析
1. 漏洞背景
本文分析了由于PHP中htmlentities()函数使用不当导致的安全漏洞,主要包括XSS(跨站脚本攻击)和SQL注入两种类型。该漏洞源于开发者对htmlentities()函数参数配置不当,未能正确处理单引号和双引号的转义。
2. htmlentities函数详解
2.1 函数定义
string htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] )
2.2 参数说明
$string: 要转换的字符串$flags: 指定如何处理引号,有三种可选值:ENT_COMPAT(默认): 只转换双引号ENT_QUOTES: 转换双引号和单引号ENT_NOQUOTES: 不转换任何引号
$encoding: 字符编码,默认为PHP配置的默认字符集$double_encode: 是否对已存在的HTML实体进行二次编码
2.3 安全影响
错误配置flags参数会导致:
- 当使用
ENT_COMPAT或ENT_NOQUOTES时,单引号可能不会被转义 - 在输出到HTML或拼接SQL语句时,可能导致XSS或SQL注入漏洞
3. 漏洞案例分析
3.1 案例1:XSS漏洞
漏洞代码片段:
foreach($_GET as $key => $value){
$value = intval($value); // 只处理value,未处理key
}
echo '<a href="'.$query.'">'.$query.'</a>';
漏洞点分析:
- 只对
$value进行类型转换,未处理$key - 输出时虽然使用了
htmlentities,但默认参数(ENT_COMPAT)不转义单引号 - 攻击者可构造恶意payload闭合单引号并注入JavaScript代码
攻击Payload:
/?a'onclick%3dalert(1)%2f%2f=c
3.2 案例2:DM企业建站系统SQL注入
漏洞代码片段:
// 登录处理代码
$user = $_POST['user'];
$query = "SELECT * FROM admin WHERE user='".$user."'";
// 过滤函数
function htmlentitiesdm($str){
return htmlentities($str, ENT_NOQUOTES); // 不转义任何引号
}
漏洞点分析:
- 使用
htmlentities()但设置ENT_NOQUOTES参数,不转义单引号 - 用户输入直接拼接到SQL语句中,导致SQL注入
- 虽然考虑了XSS防护,但错误配置导致SQL注入风险
修复方式:
将参数改为ENT_QUOTES,同时转义单引号和双引号:
function htmlentitiesdm($str){
return htmlentities($str, ENT_QUOTES);
}
4. 漏洞验证方法
4.1 XSS验证
构造包含JavaScript事件的payload,观察是否执行:
http://example.com/?a'onclick='alert(1)//
4.2 SQL注入验证
使用时间盲注技术验证:
username=admin' AND (SELECT * FROM (SELECT(SLEEP(5)))a)--
观察响应时间是否延迟5秒
5. 安全防护建议
-
正确使用htmlentities函数:
- 始终使用
ENT_QUOTES参数,确保单双引号都被转义 - 示例:
htmlentities($input, ENT_QUOTES)
- 始终使用
-
防御SQL注入:
- 使用预处理语句(PDO或MySQLi)
- 如果必须拼接SQL,确保正确转义所有引号
-
输入验证:
- 对用户输入进行严格的白名单验证
- 根据上下文使用适当的过滤函数
-
输出编码:
- 根据输出上下文(HTML/JS/URL)使用适当的编码函数
- 如
htmlspecialchars(),urlencode()等
6. CTF题目分析
题目代码:
$username = @clean((string)$_GET['username']);
$password = @clean((string)$_GET['password']);
$query='SELECT * FROM ctf.users WHERE name=\''.$username.'\' AND pass=\''.$password.'\';';
过滤函数:
function clean($str){
if(get_magic_quotes_gpc()){
$str=stripslashes($str);
}
return htmlentities($str, ENT_QUOTES);
}
解题思路:
- 虽然使用了
ENT_QUOTES转义引号,但可以通过HTML实体编码绕过 - 构造payload时使用HTML实体表示单引号:
其中username=admin&password=or 1=1 %26%23x27;--%26%23x27;是'的URL编码,在HTML中会被解码为单引号
7. 总结
htmlentities()函数使用不当会导致严重的安全漏洞- 必须使用
ENT_QUOTES参数确保单双引号都被转义 - 安全开发应遵循"输入验证、输出编码"的原则
- 在防御XSS的同时,也要考虑其他类型漏洞的防护
8. 参考资源
- PHP官方文档 - htmlentities函数
- OWASP XSS防护指南
- SQL注入防护最佳实践
- HTML实体编码表