
本文详解在 servlet 应用中对纯文本字段(如姓名、邮箱、描述)进行服务端 html/脚本过滤的最佳实践,强调“不依赖正则删除、不信任客户端、始终转义输出、优先使用 prepared statements”的四重防护原则。
本文详解在 servlet 应用中对纯文本字段(如姓名、邮箱、描述)进行服务端 html/脚本过滤的最佳实践,强调“不依赖正则删除、不信任客户端、始终转义输出、优先使用 prepared statements”的四重防护原则。
在 Web 应用开发中,当用户通过表单提交姓名、邮箱、简介等纯文本内容时,目标并非支持富文本,而是彻底拒绝任何 HTML 或脚本注入。此时,简单粗暴地用 replaceAll("<.>", "")</.> 删除标签看似有效,实则存在严重隐患:该正则无法处理换行、注释嵌套(如 <!-- <script> -->)、HTML 实体编码(如 <script></script>)、Unicode 变体或已双重编码的数据;更关键的是,它混淆了「输入过滤」与「输出转义」的职责边界——真正的安全不在于“删掉什么”,而在于“如何渲染”。
✅ 正确做法是分层防御:
-
客户端仅作体验优化(不可信)
使用 HTML5 表单属性(如type="email"、pattern)和轻量 JS 进行即时提示,例如:<input type="text" name="name" pattern="^[^<>" title="请勿输入 HTML 标签或特殊字符" required>
⚠️ 注意:前端验证可被绕过,绝不能替代服务端防护。
-
服务端严格白名单校验 + 安全转义(核心防线)
对纯文本字段,应采用白名单字符集校验(如仅允许字母、数字、常见标点),而非黑名单式删除:// 推荐:基于 Apache Commons Text 的安全清理(需引入 commons-text) String safeName = StringEscapeUtils.escapeHtml4(userInput.trim()); // 或手动白名单校验(示例:仅允许中文、英文字母、数字、空格及基础标点) if (!userInput.matches("^[\u4e00-\u9fa5a-zA-Z0-9\s\.,!?\-]+$")) { throw new IllegalArgumentException("非法字符:仅支持中英文、数字及常见标点"); } -
数据库层:Prepared Statements 是底线
即使输入含恶意内容,只要使用PreparedStatement绑定参数,SQL 注入风险即被完全阻断:String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, safeName); // 自动处理特殊字符 ps.setString(2, safeEmail); ps.executeUpdate(); } -
输出环节:永远转义,永不拼接 HTML
在 JSP/Thymeleaf 等模板中,必须使用内置转义机制:<!-- ✅ 安全:JSTL c:out 默认转义 --> <out value="${user.name}"></out><!-- ❌ 危险:直接 EL 输出(禁用 scriptlet) -->
? 关键总结:
- 不要依赖正则清洗 HTML——它无法覆盖所有攻击向量,且易引发数据损坏;
- 不要尝试“彻底清除 HTML”——这属于过度设计,反而增加误杀和维护成本;
- 真正安全 = 输入校验(白名单) + 存储安全(Prepared Statement) + 输出转义(模板引擎);
- 永远假设客户端不可信,所有防护逻辑必须在服务端强制执行。
最后提醒:若业务未来可能需要支持有限 HTML(如描述字段),应改用专业库(如 OWASP Java HTML Sanitizer)进行严格策略配置,而非自行实现过滤逻辑。安全不是功能,而是贯穿设计、编码、测试每一环的工程纪律。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











