当前位置:网站首页 >  攻略

公告内容安全防护:XSS过滤与敏感词过滤全流程实操

时间:2026年06月06日 19:39:55 来源:易频IT社区

一、环境准备与依赖引入

在开始构建公告内容防护系统之前,我们需要准备一个基础的 Spring Boot 项目环境。本指南将基于 Java 语言和 Spring Boot 框架进行演示,核心防护逻辑不依赖特定框架,可轻松移植到其他语言环境。

在项目的 pom.xml 文件中引入 Jsoup 依赖。Jsoup 是一款 Java 的 HTML 解析器,我们将利用其强大的 Whitelist(白名单)机制来过滤 XSS 攻击。

```xml org.jsoup jsoup 1.15.17 ```

确保项目能够正常加载 Maven 依赖后,我们进入核心代码的编写阶段。整个防护体系分为两个部分:XSS 过滤(防止脚本注入)和敏感词过滤(防止违规内容发布)。

二、XSS 恶意脚本过滤实现

公告系统通常允许富文本输入,以便支持排版,但这同时也给了攻击者注入 Script 标签或事件属性的机会。我们不能直接禁用所有 HTML 标签,而是需要定义一个严格的白名单,仅保留安全的标签(如 p, br, span, b)和属性。

1. 定义 XSS 清理工具类

创建一个名为 XssCleanUtil 的工具类。这个类将封装 Jsoup 的清理逻辑,并提供静态方法供业务层直接调用。

```java import org.jsoup.Jsoup; import org.jsoup.safety.Safelist; public class XssCleanUtil { / 配置安全的 HTML 白名单 仅允许基本的文本格式标签,拒绝 script, iframe, object 等危险标签 / private static final Safelist WHITELIST = Safelist.relaxed() .addTags("h1", "h2", "h3", "h4", "h5", "h6") // 允许标题 .addTags("p", "span", "div", "br") // 允许段落和换行 .addTags("ul", "ol", "li") // 允许列表 .addTags("b", "strong", "i", "em", "u") // 允许文字样式 .addAttributes("span", "style") // 允许 span 拥有 style 属性(注意:此处需配合 CSS 过滤,暂仅演示基础) .addProtocols("a", "href", "http", "https") // 仅允许 http/https 链接 .removeProtocols("a", "href", "ftp", "javascript"); // 移除危险协议 / 清理输入的 HTML 字符串 @param dirtyHtml 用户输入的原始 HTML @return 清理后的安全 HTML / public static String clean(String dirtyHtml) { if (dirtyHtml == null || dirtyHtml.trim().isEmpty()) { return ""; } // 使用 Jsoup 进行清理,OutputSettings().prettyPrint(false) 避免改变原有换行结构 return Jsoup.clean(dirtyHtml, WHITELIST) .outputSettings(new org.jsoup.nodes.Document.OutputSettings().prettyPrint(false)); } } ```

2. 配置白名单策略细节

在上述代码中,我们使用了 Safelist.relaxed() 作为基础,它允许大部分常用的文本标签。随后我们通过 addTags 显式增加了标题和列表标签,确保公告内容具备基本的可读性。

关键操作点:必须调用 removeProtocols 移除 javascript: 协议。否则,攻击者可能构造 点击 绕过标签检测。同时,我们禁用了 img 标签的 onerror 事件(Jsoup 默认会移除所有事件处理器),这是防止 XSS 触发的重要一环。

三、敏感词过滤 DFA 算法实现

除了脚本攻击,公告内容还需要过滤政治、色情或广告等敏感词。正则表达式在处理大量敏感词时性能较差,这里我们采用 DFA(确定性有限自动机)算法,将敏感词库预处理成一个 Map 结构,实现 O(n) 时间复杂度的快速匹配。

1. 准备敏感词库

在项目的 src/main/resources 目录下创建一个名为 sensitive-words.txt 的文件。每行一个敏感词,编码格式务必保存为 UTF-8。

坏蛋
赌博
私服
中奖

2. 初始化敏感词 Map

创建 SensitiveWordFilter 类。在类加载时(静态代码块)读取文件并构建 DFA 模型。

```java import java.io.; import java.util.; public class SensitiveWordFilter { // 敏感词匹配树 private static Map sensitiveWordMap; // 标识是否到达词尾 private static final String END_FLAG = "isEnd"; static { initSensitiveWordMap(); } / 初始化敏感词库,构建 DFA 模型 / private static void void initSensitiveWordMap() { sensitiveWordMap = new HashMap<>(); Set wordSet = readSensitiveWords(); for (String word : wordSet) { Map nowMap = sensitiveWordMap; for (int i = 0; i < word.length(); i++) { char keyChar = word.charAt(i); Object charMap = nowMap.get(String.valueOf(keyChar)); if (charMap != null) { nowMap = (Map) charMap; } else { Map newWordMap = new HashMap<>(); newWordMap.put(END_FLAG, false); nowMap.put(String.valueOf(keyChar), newWordMap); nowMap = newWordMap; } if (i == word.length() - 1) { nowMap.put(END_FLAG, true); } } } } / 从 resources 目录读取敏感词文件 / private static Set readSensitiveWords() { Set wordSet = new HashSet<>(); InputStream is = SensitiveWordFilter.class.getClassLoader().getResourceAsStream("sensitive-words.txt"); if (is == null) { return wordSet; } try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"))) { String line; while ((line = reader.readLine()) != null) { if (!line.trim().isEmpty()) { wordSet.add(line.trim()); } } } catch (IOException e) { e.printStackTrace(); } return wordSet; } ```

3. 编写检测逻辑

在同一个类中添加检测方法。该方法遍历输入文本的每一个字符,在 DFA 树中查找路径。如果发现 isEnd 标记为 true,则说明命中敏感词。

```java / 判断文本是否包含敏感词 @param text 待检测文本 @return true 包含,false 不包含 / public static boolean contains(String text) { if (text == null || text.trim().isEmpty()) { return false; } for (int i = 0; i < text.length(); i++) { int matchFlag = checkSensitiveWord(text, i); if (matchFlag > 0) { return true; // 发现敏感词,直接返回 } } return false; } / 从指定位置开始检查 @param text 全文 @param beginIndex 开始索引 @return 匹配到的敏感词长度,0表示未匹配 / private static int checkSensitiveWord(String text, int beginIndex) { boolean flag = false; int matchLength = 0; Map nowMap = sensitiveWordMap; for (int i = beginIndex; i < text.length(); i++) { char keyChar = text.charAt(i); nowMap = (Map) nowMap.get(String.valueOf(keyChar)); if (nowMap != null) { matchLength++; if (Boolean.TRUE.equals(nowMap.get(END_FLAG))) { flag = true; // 这里可以根据需求选择:是发现即返回,还是继续寻找最长匹配 // 此处选择发现即返回,提高效率 break; } } else { break; } } return flag ? matchLength : 0; } / 替换敏感词为指定符号(可选功能) / public static String replace(String text, String replaceChar) { if (contains(text)) { // 简单实现:实际项目中建议结合 StringBuilder 进行高性能替换 // 这里为了代码简洁,仅做演示,实际应记录匹配位置进行替换 // 若需完整替换代码,需将 contains 逻辑改为返回 List throw new RuntimeException("内容包含敏感词,已被拦截。"); } return text; } } ```

四、业务层集成与全流程测试

现在我们将 XSS 过滤和敏感词过滤集成到业务逻辑中。我们需要先进行 XSS 清理(净化 HTML),再进行敏感词检测(拦截违规语义)。

1. Controller 层接口定义

创建一个 NoticeController,模拟接收前端提交的公告内容。

```java import org.springframework.web.bind.annotation.; @RestController @RequestMapping("/api/notice") public class NoticeController { @PostMapping("/publish") public String publishNotice(@RequestBody NoticeDto noticeDto) { String rawContent = noticeDto.getContent(); // 步骤1:XSS 过滤 // 将危险的 HTML 标签和属性剥离,保留安全的富文本格式 String safeContent = XssCleanUtil.clean(rawContent); // 步骤2:敏感词检测 // 对过滤后的文本进行语义检测 if (SensitiveWordFilter.contains(safeContent)) { return "发布失败:内容包含违规敏感词,请检查后重试。"; } // 步骤3:业务逻辑处理(如保存数据库) // saveToDatabase(noticeDto.getTitle(), safeContent); return "发布成功!安全内容已入库。"; } // 简单的 DTO 类 static class NoticeDto { private String title; private String content; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } } ```

2. 测试用例验证

启动 Spring Boot 项目,使用 Postman 或 curl 发送 POST 请求进行测试。

测试用例 1:XSS 攻击拦截

请求 Body(JSON):

{
"title": "系统升级通知",
"content": "

系统将于今晚维护,请知悉。

" }

预期结果:接口返回“发布成功”。查看数据库或日志,会发现内容已被清洗为:

系统将于今晚维护,请知悉。

script 标签已被完全移除,且保留了 p 标签。

测试用例 2:敏感词拦截

请求 Body(JSON):

{
"title": "活动推广",
"content": "

点击链接参与赌博活动,大奖等你拿

" }

预期结果:接口返回“发布失败:内容包含违规敏感词,请检查后重试。”。因为“赌博”一词在 sensitive-words.txt 中已定义,DFA 算法成功匹配并拦截。

测试用例 3:组合攻击绕过尝试

攻击者可能尝试将敏感词拆分或利用 HTML 实体编码绕过。例如:

{
"title": "测试",
"content": "
" }

预期结果:Jsoup 的 clean 方法会移除带有 onerror 的 img 标签,将其变为纯文本或直接删除。如果最终剩余文本包含“中奖”且词库中有该词,同样会被拦截。这展示了多层防护的必要性。

相关推荐

最新

热门

推荐

精选

标签

易频IT社区是综合性互联网IT技术门户网站,专注分享网络技术、服务器运维、网络安全、编程开发、系统架构、云计算、大数据等行业干货,实时更新IT行业资讯、零基础教程、实战案例,为IT从业者、技术爱好者提供专业的学习交流平台。

Copyright © 2021-2026 易频IT社区. All Rights Reserved. 备案号:闽ICP备2023013482号 网站地图