本文目录导读:

过滤和拦截特殊字符是Web开发和数据安全中的常见需求,主要目的是防止注入攻击(如SQL注入、XSS跨站脚本攻击)、路径遍历以及数据格式错误。
根据你使用的场景(前端、后端、数据库)和技术栈,方法有所不同,以下整理了从客户端到服务端、从通用到特定的多层防护策略:
核心原则:输出编码 优于 输入过滤
现代安全最佳实践不建议简单地“删除”或“拦截”特殊字符,因为很多特殊字符在业务中是合法输入(如用户名允许下划线 )。
- 白名单过滤(推荐): 只允许你明确知道的“安全字符”,其余的全部拒绝。
- 黑名单过滤(不推荐): 试图阻止已知的“危险字符”(如
<、>、、、),很容易被绕过。
最重要的原则:
- 存储时: 保留原始数据(除非有严格的格式要求)。
- 输出/展示时: 根据上下文(HTML、JavaScript、SQL、URL)进行转义或编码。
前端过滤(浏览器端)
前端拦截主要是为了提升用户体验(防止误输入)和减轻服务器压力,不能作为安全依仗(很容易被开发者工具或抓包绕过)。
A. 使用 input 标签的 pattern 属性(正则)
只允许输入字母、数字。
<input type="text" pattern="[a-zA-Z0-9]+" title="只允许英文字母和数字">
B. JavaScript 实时过滤(阻止按键)
// 在输入框的 keydown 或 input 事件中拦截
document.getElementById('myInput').addEventListener('keydown', function(e) {
const key = e.key;
// 只允许字母、数字、退格键
const allowed = /[a-zA-Z0-9]|Backspace|Delete|Arrow/;
if (!allowed.test(key) && !e.ctrlKey && !e.metaKey) {
e.preventDefault(); // 阻止输入
}
});
// 或者直接替换字符(在 input 事件中)
document.getElementById('myInput').addEventListener('input', function(e) {
// 移除所有非字母数字字符
this.value = this.value.replace(/[^a-zA-Z0-9]/g, '');
});
C. 第三方库
- DOMPurify: 专门用于清理 HTML,防止 XSS。
- XSS.js: 用于检测和过滤潜在的 XSS 代码。
后端过滤(服务端 - 最重要的一环)
后端是数据入口的最后一道防线,必须假设所有前端数据都是“脏数据”。
A. 通用输入验证(针对特定字段格式)
根据字段类型做验证是最安全的方式。
- 邮箱:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ - 手机号: 根据国家/地区正则匹配数字。
- 用户名: 只允许
[a-zA-Z0-9_]。 - ID/主键: 强制转换为整数或UUID格式。
所有不匹配的格式,直接返回错误 400 Bad Request,不要尝试修改用户输入。
B. SQL注入防护(最危险的特殊字符场景)
绝对不要手动过滤 、、 等字符来拼接SQL。
- 正确做法:使用参数化查询(Prepared Statements)。
- Java (JDBC):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?"); - Python (SQLAlchemy/psycopg2):
cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,)) - Node.js (mysql2):
connection.execute('SELECT * FROM users WHERE id = ?', [userId])
- Java (JDBC):
C. XSS跨站脚本防护(针对 <script>、<、> 等)
- 方法:HTML 实体编码。
- 将
<编码为< - 将
>编码为> - 将 编码为
"
- 将
- 对应库:
- Java:
StringEscapeUtils.escapeHtml4()(Apache Commons Text) - Python:
html.escape(user_input) - Node.js:
escape-html库
- Java:
- 最佳实践: 使用模板引擎(如 Thymeleaf, Jinja2, React JSX)时,它们默认会进行输出编码,不要手动拼接HTML字符串。
D. 操作系统命令注入(用于 cmd、bash)
- 禁止直接将用户输入传入系统命令。
- 如果必须执行命令,使用严格的白名单限制命令和参数。
数据库层面(辅助防御)
可以在数据库层面做一次拦截,作为深度防御。
A. 存储过程
使用存储过程访问数据,应用层只传递参数,不拼接SQL,这能有效隔离数据库操作。
B. 字符集设置
始终使用 UTF-8 或 Unicode 字符集,设置数据库连接为:
SET NAMES 'utf8mb4';
这可以防止部分针对字符集的注入攻击(如宽字节注入)。
C. 字段约束
- 长度限制(VARCHAR): 限制输入长度,防止缓冲区溢出或长字符串注入(如
CHAR(10))。 - 数据类型约束: 数字列就定义为
INT,不要试图存字符串。
常用特殊字符的黑名单(供参考 - 用于极端严格场景)
如果你确实需要“拦截”特定字符,可以检查以下列表,但再次强调,白名单过滤比黑名单更安全。
| 场景 | 危险字符 | 建议处理方式 |
|---|---|---|
| SQL注入 | 不要过滤,用参数化查询 | |
| XSS (HTML) | < > & |
转义为HTML实体 |
| XSS (JavaScript) | < > |
转义为Unicode或Hex编码 |
| 路径遍历 | %00 (NULL字节) |
拒绝,或使用路径白名单 |
| 命令注入 | | & \n |
禁止执行用户输入的命令 |
| JSON/XML | 等结构化字符 | 使用JSON库序列化/反序列化,不要手动拼接 |
最佳实践流程图
- 用户输入 → 到达后端
- 白名单格式验证
- → 失败:返回 400 错误(“格式不正确”)
- → 成功:进入下一步
- 参数化查询(如果是数据库操作)或 编码/转义(如果是页面展示)
- 数据库存储(原始数据)
- 前端展示 → 输出编码(HTML实体转义)
一句话总结:
在前端做输入限制(提升体验),在后端做白名单验证(保证安全),在数据库用参数化查询(防御注入),在输出时做编码转义(防御XSS)。 永远不要相信原始输入。