课程笔记:XSS攻击的输入环节防护
课程名称:计算机网络应用 核心摘要:本讲承接上一讲XSS漏洞防护策略的宏观框架,聚焦于"输入环节"的具体实现。讲解如何通过自定义Filter + HttpServletRequestWrapper重写参数获取方法,对用户输入做半角转全角、HTML转义等处理;并介绍OWASP AntiSamy、Apache Commons Text两种开源方案的原理与组合用法,最终在留言板场景中验证防护效果。
一、 核心概念与原理
- XSS防护四大策略中,本讲聚焦第一项:输入环节防护。
- 防护落点:作为开发人员,应在**后台(后端)**执行两类校验:
- 用户录入数据的长度判断
- 特殊字符的转义(转移/转义)处理
- 部署位置:通常定义在**流量网关(Gateway)**部分做统一拦截。
- 实现思路:通过过滤器(Filter)实现全局过滤,拦截所有请求并区分处理。
- 核心机制:利用包装器模式(Wrapper),将原生
HttpServletRequest封装为自定义的XssHttpServletRequestWrapper,在后续调用链中传递被处理过的请求对象。 - 转义原理:将半角特殊字符转为全角字符,使其丧失HTML/JS语义,浏览器不再当作脚本执行。
二、 技术细节与协议分析
2.1 三大开源/自实现方案对比
| 方案 | 来源 | 典型类/方法 | 适用场景 | 特点 |
|---|---|---|---|---|
自定义编码器 XssEncode1 | 自研 | xssEncode(String) | 通用参数转义 | 半角转全角,灵活可控 |
| OWASP AntiSamy | OWASP | AntiSamy.scan() | 富文本/HTML清洗 | 自带 antisamy.xml 规则配置 |
| Apache Commons Text | Apache | StringEscapeUtils.escapeHtml4() | HTML转义 | 也支持JSON/Script转义 |
三种方案可组合使用,根据数据类型(纯文本/JSON/脚本)灵活切换。
2.2 AntiSamy 命名由来
- Anti:反对、对抗
- Samy:人名,指 Samy Kamkar,在 MySpace 网站制造了第一个 XSS蠕虫攻击
- 命名含义:反对此类XSS蠕虫,象征安全防护
2.3 需重写的 HttpServletRequestWrapper 方法
| 重写方法 | 功能说明 | 防护必要性 |
|---|---|---|
getParameter(name) | 按参数名获取单值 | 最常见入口,必做转义 |
getParameterMap() | 获取所有参数键值对 | 通过迭代器遍历逐值转义 |
getParameterValues(name) | 按参数名获取多值数组 | 每个值均需校验 |
getHeaders() | 获取请求头 | HTTP/1.1后Header使用频繁,防Header注入 |
getInputStream() | 流方式读取Body | 防请求体注入 |
getRequestBody() | 读取JSON串 | 防JSON Body注入 |
2.4 静态资源白名单(排除名单)
通过 request.getServletPath() 获取请求路径,对以下静态资源不做处理,直接放行:
| 资源类型 | 后缀示例 |
|---|---|
| 脚本 | .js |
| 图片 | .jpg .gif .png |
| 文档 | .pdf |
| 样式 | .css |
| 图标 | .ico |
2.5 自定义转义规则(半角→全角)
| 半角字符 | 全角字符 | 说明 |
|---|---|---|
> | > | 大于号 |
< | < | 小于号 |
' | ' | 单引号 |
" | " | 双引号 |
转换后语义丢失,即便包含JS脚本也不会被浏览器执行。
三、 实践应用与配置命令
3.1 Maven 依赖引入
<!-- OWASP AntiSamy:富文本/HTML清洗 -->
<dependency>
<groupId>org.owasp</groupId>
<artifactId>antisamy</artifactId>
</dependency>
<!-- Apache Commons Text:HTML/JSON/Script转义 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
3.2 Filter 骨架结构
// 自定义过滤器名称
public class XssFilter implements Filter {
// 拦截所有请求(url-pattern: /*)
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
String path = request.getServletPath();
// 1. 静态资源白名单:JS/JPG/GIF/PNG/PDF/CSS/ICO 直接放行
if (isStaticResource(path)) {
chain.doFilter(req, resp);
return;
}
// 2. 非静态资源:包装为自定义Request,后续链条传递XssRequest
chain.doFilter(new XssHttpServletRequestWrapper(request), resp);
}
}
3.3 自定义 Wrapper 重写示例
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request); // 调用父类构造,持有原生request
}
@Override
public String getParameter(String name) {
String value = super.getParameter(name);
return (value == null) ? null : XssEncode1.xssEncode(value);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = super.getParameterMap();
// 迭代器遍历,逐值调用 xssEncode 转义
// ...返回处理后的新Map
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
// 对每个元素做转义
}
@Override
public Enumeration<String> getHeaders(String name) {
// 防Header注入:对Header值转义
}
// getInputStream / getRequestBody:针对JSON Body转义
}
3.4 三种编码器调用切换
// 方案1:自定义半角转全角
XssEncode1.xssEncode(value);
// 方案2:OWASP AntiSamy(静态块加载antisamy.xml → scan → 输出)
AntiSamy antiSamy = new AntiSamy();
CleanResults results = antiSamy.scan(value, policy);
String clean = results.getCleanHTML();
// 方案3:Apache Commons Text
StringEscapeUtils.escapeHtml4(value); // HTML转义
StringEscapeUtils.escapeJson(value); // JSON转义(按需)
3.5 演示验证流程
- 重启项目使过滤器生效
- 留言板提交弹窗型XSS payload(如
<script>alert(1)</script>) - 重新登录(账号
admin/ 密码123456) - 查询留言 → 看到内容已被转义输出,脚本不再执行
四、 重点与难点提示
- 考点1:XSS输入防护应放在后端做长度限制 + 特殊字符转义,而非仅靠前端。
- 考点2:**白名单(排除名单)**思想——静态资源放行,动态请求才过滤。
- 考点3:Wrapper模式是Servlet规范中扩展请求处理的标准做法,务必重写所有取参方法(含Header/Body)。
- 易错点:只重写
getParameter而漏掉getInputStream/getRequestBody,会导致JSON接口仍可被注入。 - 易错点:混淆半角/全角转换与HTML实体转义——前者破坏语义,后者编码语义,二者均有效但机制不同。
- 面试题:AntiSamy的命名来源?答:反对Samy(MySpace首个XSS蠕虫作者)。
- 面试题:三种方案能否组合?答:可以,依据数据载体(纯文本/HTML/JSON/Script)灵活选择。
- 难点:生产环境中防护通常部署在流量网关,演示中用自定义Filter替代以简化场景。
五、 课后疑问/遗留问题
- 自定义
antisamy.xml规则文件如何编写?如何针对富文本场景做精细化白名单配置? - 半角转全角方案对业务语义是否有副作用(如用户确实需要输入
<>符号)?如何兼顾可用性? - XSS防护的另外三种策略(输出编码、CSP、HttpOnly Cookie)将在后续课程展开。
- AntiSamy与OWASP Java Encoder(Encoder)的区别与选型?