特殊字符如何过滤拦截

wen 网络安全 32

本文目录导读:

特殊字符如何过滤拦截

  1. 核心原则:输出编码 优于 输入过滤
  2. 前端过滤(浏览器端)
  3. 后端过滤(服务端 - 最重要的一环)
  4. 数据库层面(辅助防御)
  5. 常用特殊字符的黑名单(供参考 - 用于极端严格场景)
  6. 最佳实践流程图

过滤和拦截特殊字符是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])

C. XSS跨站脚本防护(针对 <script><> 等)

  • 方法:HTML 实体编码。
    • < 编码为 &lt;
    • > 编码为 &gt;
    • 将 编码为 &quot;
  • 对应库:
    • Java: StringEscapeUtils.escapeHtml4() (Apache Commons Text)
    • Python: html.escape(user_input)
    • Node.js: escape-html
  • 最佳实践: 使用模板引擎(如 Thymeleaf, Jinja2, React JSX)时,它们默认会进行输出编码,不要手动拼接HTML字符串。

D. 操作系统命令注入(用于 cmdbash

  • 禁止直接将用户输入传入系统命令。
  • 如果必须执行命令,使用严格的白名单限制命令和参数。

数据库层面(辅助防御)

可以在数据库层面做一次拦截,作为深度防御。

A. 存储过程

使用存储过程访问数据,应用层只传递参数,不拼接SQL,这能有效隔离数据库操作。

B. 字符集设置

始终使用 UTF-8Unicode 字符集,设置数据库连接为:

SET NAMES 'utf8mb4';

这可以防止部分针对字符集的注入攻击(如宽字节注入)。

C. 字段约束

  • 长度限制(VARCHAR): 限制输入长度,防止缓冲区溢出或长字符串注入(如 CHAR(10))。
  • 数据类型约束: 数字列就定义为 INT,不要试图存字符串。

常用特殊字符的黑名单(供参考 - 用于极端严格场景)

如果你确实需要“拦截”特定字符,可以检查以下列表,但再次强调,白名单过滤比黑名单更安全

场景 危险字符 建议处理方式
SQL注入 不要过滤,用参数化查询
XSS (HTML) < > & 转义为HTML实体
XSS (JavaScript) < > 转义为Unicode或Hex编码
路径遍历 %00 (NULL字节) 拒绝,或使用路径白名单
命令注入 | & \n 禁止执行用户输入的命令
JSON/XML 等结构化字符 使用JSON库序列化/反序列化,不要手动拼接

最佳实践流程图

  1. 用户输入 → 到达后端
  2. 白名单格式验证
    • → 失败:返回 400 错误(“格式不正确”)
    • → 成功:进入下一步
  3. 参数化查询(如果是数据库操作)或 编码/转义(如果是页面展示)
  4. 数据库存储(原始数据)
  5. 前端展示输出编码(HTML实体转义)

一句话总结:

在前端做输入限制(提升体验),在后端做白名单验证(保证安全),在数据库用参数化查询(防御注入),在输出时做编码转义(防御XSS)。 永远不要相信原始输入。

抱歉,评论功能暂时关闭!