简介
本指南面向DouPHP项目的XSS(跨站脚本)防护,围绕“输入过滤、输出编码、内容安全策略CSP”三大维度,结合框架内置的XSS工具类与安全响应头中间件,提供可落地的配置与最佳实践。文档重点包括:
- 使用内置XSS工具进行安全的HTML白名单过滤与属性校验
- 在模板渲染时进行上下文感知的输出编码
- 通过安全响应头中间件下发基线安全头(不含CSP),并给出CSP落地建议
- 针对JavaScript事件处理器、URL协议、样式等高风险场景的处理方案
- 常见攻击场景与防御策略对照表
项目结构
与XSS防护直接相关的代码主要分布在以下位置:
- 核心XSS过滤能力:core/infra/Security/Xss.php
- 门面与辅助函数:core/facade/Xss.php、core/foundation/container/helpers.php
- 安全响应头中间件:core/foundation/middleware/AbstractSecurityHeadersMiddleware.php 及各端薄壳实现(如 front/middleware/SecurityHeadersMiddleware.php)
- 安全配置:config/security.php
graph TB
A["控制器/服务"] --> B["xss() 或 Xss 门面"]
B --> C["Xss::filterHtml / comment / content / rss"]
A --> D["视图渲染"]
D --> E["模板变量输出编码"]
A --> F["安全响应头中间件"]
F --> G["安全头: X-Frame-Options / Referrer-Policy / Permissions-Policy / HSTS(可选)"]
核心组件
- Xss 工具类(核心过滤引擎)
- 提供 filterHtml、comment、content、rss 等方法,支持安全模式、标签白名单、属性白名单、URL协议白名单、样式过滤、嵌套合法性检查、标签平衡等。
- 关键配置项:safe、elements、deny_elements、deny_attribute、safe_allow、parent、remove_comments、remove_cdata、clean_control_chars、balance_tags、keep_bad。
- Xss 门面与 xss() 辅助函数
- 通过容器解析 Xss 实例,便于在控制器/服务中统一调用。
- 安全响应头中间件
- 从 config/security.php 读取 headers 配置,在管道前置下发基线安全头(不含 CSP)。HSTS 仅在 HTTPS 且开启时下发。
架构总览
下图展示请求进入后,XSS防护在“输入过滤—渲染—响应头”三阶段的协作方式。
sequenceDiagram
participant U as "用户"
participant C as "控制器/服务"
participant X as "Xss 工具"
participant V as "模板渲染器"
participant M as "安全响应头中间件"
participant R as "浏览器"
U->>C : 提交表单/参数
C->>X : 调用 xss()->filterHtml/comment/content/rss
X-->>C : 返回已过滤的HTML/文本
C->>V : 将安全数据传入模板
V-->>R : 输出HTML配合上下文编码
C->>M : 生成响应
M-->>R : 下发安全响应头X-Frame-Options/Referrer-Policy/Permissions-Policy/HSTS
详细组件分析
Xss 工具类:输入过滤与白名单
- 处理流程概览
- 清理BOM/空字节/控制字符
- 移除DOCTYPE、注释、CDATA
- 实体规范化
- 标签扫描与白名单校验(含 safe/safe_allow/elements/deny_elements)
- 属性白名单校验(全局+标签专属+aria-/data-前缀)
- URL协议白名单校验(含 data:image/* 特例)
- style 属性深度过滤(url()、危险关键字、转义绕过)
- srcset 多值URL过滤
- 标签嵌套合法性检查与闭合平衡
- 关键方法
- filterHtml(html, config):通用HTML过滤入口
- comment(html):评论场景的严格白名单
- content(html):内容场景允许更多标签
- rss(html):RSS场景的有限标签集
- 白名单与限制要点
- 默认安全模式下禁止大量危险标签(script、iframe、object、svg、math等)
- 仅允许特定URL协议(http/https/mailto/tel/ftp/ftps 等),对 javascript/vbscript 等危险协议严格拒绝
- 对 on* 事件处理器一律丢弃
- style 中 url() 仅允许 http/https,data:image/* 白名单放行
- 支持 safe_allow 与 elements 精细控制
flowchart TD
Start(["开始"]) --> Clean["清理BOM/空字节/控制字符"]
Clean --> Strip["移除DOCTYPE/注释/CData"]
Strip --> Normalize["实体规范化"]
Normalize --> Scan["扫描标签并校验白名单"]
Scan --> Attrs{"属性合法?"}
Attrs -- 否 --> Drop["丢弃非法属性/标签"]
Attrs -- 是 --> UrlCheck{"是否URL属性?"}
UrlCheck -- 是 --> UrlFilter["协议白名单/危险协议检测"]
UrlCheck -- 否 --> StyleCheck{"是否style?"}
StyleCheck -- 是 --> StyleFilter["过滤url()/危险关键字/转义"]
StyleCheck -- 否 --> Nesting{"嵌套合法?"}
Nesting -- 否 --> Drop
Nesting -- 是 --> Balance["标签平衡"]
Balance --> End(["结束"])
门面与辅助函数:便捷调用
- xss() 辅助函数:从容器解析 Xss 单例,供业务层统一调用
- Xss 门面:静态门面,底层委托到 Xss 类,提供 setConfig/getConfig/resetConfig/filterHtml/comment/content/rss/text/post 等静态方法
安全响应头中间件:基线安全头
- 行为说明
- 从 config/security.php 的 security.headers 读取配置
- 下发 X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy
- HSTS 仅在 HTTPS 且 enabled=true 时下发
- 仅覆盖命中路由的请求(404自渲染不在范围内)
- 配置项
- frame_options、content_type_options、referrer_policy、permissions_policy、hsts.enabled/max_age/subdomains
classDiagram
class AbstractSecurityHeadersMiddleware {
+handle(next) mixed
-sendHeaders(headers) void
}
class SecurityHeadersMiddleware_Front {
}
AbstractSecurityHeadersMiddleware <|-- SecurityHeadersMiddleware_Front
模板输出编码:上下文感知
- 原则
- 所有用户可控数据在模板输出时必须进行上下文相关编码:HTML正文、HTML属性、JavaScript、CSS、URL
- 建议
- 优先使用模板引擎提供的自动转义功能;若需输出富文本,先经 Xss 过滤再放入模板
- 避免在模板中拼接JS/CSS字符串;如需动态注入,使用结构化数据并通过安全API渲染
依赖关系分析
- 调用链
- 业务层通过 xss() 或 Xss 门面调用 core/infra/Security/Xss.php 中的过滤逻辑
- 安全响应头由 AbstractSecurityHeadersMiddleware 在各端薄壳中统一下发
- 外部依赖
- 配置来自 config/security.php
- 中间件依赖 Request 的安全判断(如 isSecure)
graph LR
H["helpers.php<br/>xss()"] --> F["facade/Xss.php"]
F --> I["infra/Security/Xss.php"]
C["config/security.php"] --> M["AbstractSecurityHeadersMiddleware"]
M --> S["各端 SecurityHeadersMiddleware"]
性能考量
- 过滤成本
- 正则扫描与多次解码(URL/实体)会带来CPU开销,建议在批量处理时复用配置或缓存结果
- 白名单粒度
- 过宽的 elements=* 会提升复杂度;尽量限定允许的标签集合
- 中间件
- 安全响应头中间件在管道前置执行,开销极低
故障排查指南
- 富文本被过度过滤
- 检查 Xss 配置:safe、elements、deny_elements、safe_allow
- 确认是否需要启用非安全模式下的额外标签(谨慎)
- 链接无法跳转或报错
- 检查 href/src/action 等URL属性是否使用了被拒绝的协议(如 javascript)
- 确认是否在 style 中使用了不被允许的 url()
- 页面布局错乱
- 检查 balance_tags 与标签嵌套规则导致的闭合调整
- 安全头未生效
- 确认中间件已注册且命中路由
- 检查 config/security.php 的 headers 配置是否正确
结论
DouPHP提供了完善的XSS防护基础设施:以 Xss 工具为核心的输入过滤、以安全响应头中间件为基础的基线安全头下发,以及模板层的上下文编码要求。遵循“最小权限白名单、严格协议校验、禁用事件处理器、安全头兜底”的原则,可有效抵御常见XSS攻击。对于需要富文本的场景,应结合业务需求精细化配置,并在前后端协同下确保输出安全。
附录
常用配置与用法速查
- 获取XSS工具
- 使用 xss() 辅助函数或 Xss 门面
- 过滤富文本
- 使用 content(html) 或自定义 filterHtml(html, config)
- 评论/短文本
- 使用 comment(html)
- RSS内容
- 使用 rss(html)
- 安全响应头
- 在 config/security.php 中配置 security.headers
常见XSS攻击场景与防护方案
- 反射型XSS(URL参数回显)
- 输入:xss()->filterHtml() 或 xss()->text()
- 输出:模板中按上下文编码
- 存储型XSS(评论/留言)
- 输入:xss()->comment() 或 content()
- 输出:模板中按上下文编码
- 基于DOM的XSS(前端拼接)
- 后端输出必须编码;前端避免 innerHTML 拼接用户数据
- 事件处理器注入(on*)
- 过滤器会丢弃 on* 属性;模板中不要拼接事件处理器
- 危险协议(javascript:)
- URL属性会被拒绝;style 中 url() 仅允许 http/https 与 data:image/*
- CSS注入
- style 属性经过深度过滤,移除危险关键字与转义绕过