前言:本文仅用于合法授权的渗透测试场景,旨在帮助开发者识别XSS漏洞、提升Web应用安全性,严禁用于未授权攻击!网络安全无小事,违规操作将承担相应法律责任。

一、htmlspecialchars 函数介绍

htmlspecialchars() 是 PHP 中用于防御跨站脚本攻击(XSS)的一个核心函数。

它的主要作用是将 HTML 中具有特殊含义的字符转换为对应的 HTML 实体(HTML Entities)。这样,浏览器在解析时会将这些代码视为普通的文本字符来显示,而不会将其作为 HTML 标签或脚本执行。

⚙️ 函数工作原理

htmlspecialchars() 函数会查找字符串中的以下预定义字符,并将它们转换为 HTML 实体:

原始字符 转义后实体 说明
& & 转义和号,防止被解析为 HTML 实体的开始。
" " 转义双引号(仅在 ENT_NOQUOTES 模式下不转义)。
' ' 转义单引号(仅在 ENT_QUOTES 模式下转义)。
< < 转义小于号,防止被解析为 HTML 标签的开始。
> > 转义大于号,防止被解析为 HTML 标签的结束。

例如:

// 输入的字符串包含 HTML 标签
$input = '<script>alert("XSS")</script>';

// 经过 htmlspecialchars 处理
$output = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

// 输出结果(浏览器会显示为纯文本,而非执行脚本)
echo $output; 
// 浏览器显示: <script>alert("XSS")</script>

🛠️ 函数语法与参数

htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string|null $encoding = ini_get("default_charset") [, bool $double_encode = true ]]] ) : string
  1. $string (必需)
    • 待转义的原始字符串。
  2. $flags (可选)
    • 位掩码,用于指定转义的处理细节,特别是如何处理引号。常用的常量包括:
      • ENT_COMPAT: 默认值。仅转义双引号,不转义单引号。
      • ENT_QUOTES: 强烈推荐。同时转义双引号和单引号。这在防御 XSS 时至关重要,因为许多攻击利用单引号绕过过滤。
      • ENT_NOQUOTES: 不转义任何引号。
      • ENT_HTML401, ENT_HTML5: 指定处理代码时遵循的 HTML 文档类型标准。
  3. $encoding (可选)
    • 规定要使用的字符编码。强烈建议显式指定为 'UTF-8',以避免因编码不一致导致的潜在安全问题或乱码。
  4. $double_encode (可选)
    • 指定是否对已存在的 HTML 实体进行再次转义。默认为 true(进行转义)。如果设为 false,则不会对已有的实体(如 &)进行二次编码。

🛡️ 正确用法与最佳实践

为了确保最大程度的安全,调用 htmlspecialchars() 时应遵循以下最佳实践:

  • 始终使用 ENT_QUOTES: 这可以防止攻击者利用单引号绕过防御。
  • 显式指定字符集: 始终传入 'UTF-8' 作为编码参数。
  • 在输出时转义: 转义操作应在数据输出到 HTML 页面时进行,而不是在数据存入数据库时进行。这保证了数据的原始性,并针对不同的输出上下文(HTML正文、属性、JavaScript等)采取不同的编码策略。

推荐的调用方式:

// 安全且标准的用法
echo htmlspecialchars($userInput, ENT_QUOTES | ENT_HTML5, 'UTF-8');

⚖️ 与 htmlentities() 的区别

htmlspecialchars() 只转义上面列出的 5 个特殊字符。而 htmlentities() 函数会尝试将字符串中所有具有 HTML 实体对应形式的字符都进行转义,包括非 ASCII 字符(如中文、Emoji 等)。在绝大多数 Web 开发场景下,使用 htmlspecialchars() 就足够了,且能更好地保留原始文本的可读性。

二、靶场环境

请添加图片描述

通过观察前端代码,可以发现,输入框的内容会被解析到一个 a 标签的 href 特征中。由于htmlspecialchars函数的作用,输入 <script>alert(1)</script>点击后,会弹出警告。之前的 playload 已经无法发挥作用。

三、 kali 复现

htmlspecialchars 的情况下,传统的 <script> 标签注入是无效的,因为 <> 会被转义。

实现 alert 的核心思路是利用 JavaScript 伪协议事件处理器,而不是依赖 <script> 标签。

以下是几种实现 alert('xss') 的具体方法:

1. 最直接的方法:javascript: 伪协议

这是最简单、最有效的方法。只要 htmlspecialchars 没有过滤 javascript: 这个协议名,你就可以直接在 href 中执行 JS 代码。

  • Payload:

    javascript:alert('xss')
    
  • 原理:当用户点击这个链接时,浏览器不会跳转页面,而是执行 javascript: 后面的代码。

  • 绕过技巧:如果网站过滤了alert关键字,可以使用promptconfirm替代,或者使用编码。

    • 使用prompt

      javascript:prompt('xss')
      
    • 使用 Unicode 编码(绕过关键字过滤):

      javascript:\u0061\u006c\u0065\u0072\u0074('xss')
      

2. 利用事件处理器(如果引号被截断)

根据你之前的描述,如果输入被放在 <a href="..."> 中,但解析时出现了引号错乱(例如 href="javascript:..." 变成了 href="javascript:" ...),你可以利用这一点注入新的属性。

  • 场景:假设后端代码是<a href="<?php echo $input; ?>">,如果你输入:

    " onclick="alert('xss)
    
  • 解析结果:

    <a href="" onclick="alert('xss)">
    
  • 原理:你输入的双引号 " 闭合了原有的 href 属性,然后你注入了一个新的 onclick 事件。当用户点击链接时,alert 就会执行。

3. 利用 data: 协议

如果 javascript: 被过滤,或者你想执行更复杂的代码,可以使用 data: 协议。它允许你直接在 URL 中嵌入 HTML/JS 代码。

  • Payload:

    data:text/html,<script>alert('xss')</script>
    
  • 原理:浏览器会将 data: 后面的内容解析为 HTML 文档。在这个文档中,<script> 标签是合法的,因此 alert 会执行。

总结

方法 Payload 示例 适用场景
javascript: 伪协议 javascript:alert('xss') 推荐。最简单,只要协议未被过滤即可。
事件注入 " onclick="alert('xss) 适用于引号被截断或可闭合的场景。
data: 协议 data:text/html,<script>alert('xss')</script> 适用于 javascript: 被过滤的场景。

建议:首先尝试 javascript:alert('xss')。如果不行,检查是否被编码或过滤,再尝试 data: 协议。