输入校验是 Web 安全的第一道防线,但也是最容易被错误理解的一道。很多团队把校验全部压在前端,后端只做“信任上游”的简单处理;也有团队把校验全部放在网关,认为 WAF 能解决一切。现实是:输入校验必须分层,每层只解决自己能解决的问题,不能互相替代。本文从 XSS、SQL 注入和服务器加固三个角度,说明前端、后端与网关各自应该承担什么边界。
一、XSS 的常见方式与前端防御
XSS 的本质是用户输入被当作代码执行。常见方式有三类:
- 反射型 XSS:恶意脚本作为请求参数传入,服务器直接拼接到 HTML 返回。例如搜索页把
keyword原样输出到页面。 - 存储型 XSS:恶意内容被存入数据库,其他用户访问时触发。常见于评论、昵称、文章摘要。
- DOM 型 XSS:前端 JavaScript 直接操作
innerHTML、document.write、eval等,把不可信数据写入 DOM。
前端防御的核心不是“过滤所有特殊字符”,而是在输出点做正确的编码和上下文隔离:
- 使用
textContent代替innerHTML,避免把字符串当 HTML 解析。 - 如果必须插入 HTML,使用 DOMPurify 等成熟库做白名单净化,而不是自己写正则。
- 对 URL 参数、
location.hash、postMessage来源做校验,避免直接进入eval或setTimeout字符串形式。 - 设置 CSP(内容安全策略),限制内联脚本和外部脚本来源,作为纵深防御。
但前端防御有一个根本局限:攻击者可以完全绕过前端。用 curl、Burp Suite 直接构造请求,前端校验形同虚设。因此,XSS 的最终防线必须在后端输出编码和网关层。
二、MySQL 防止 SQL 注入的几种写法
SQL 注入的根源是用户输入被拼进 SQL 语句结构。防止注入不是靠“转义引号”,而是靠让输入永远不进入 SQL 语法层。以下是几种正确写法,按推荐程度排序:
1. 参数化查询(Prepared Statement)
这是最根本的解法。以 PHP PDO 为例:
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email AND status = :status');
$stmt->execute(['email' => $email, 'status' => $status]);
Java 的 PreparedStatement、Python 的 cursor.execute(sql, params)、Node.js 的 mysql2 占位符同理。参数化查询让数据库把输入当作数据,而不是 SQL 片段。
2. 存储过程(需内部也使用参数化)
存储过程本身不自动防注入。如果存储过程内部用 CONCAT 拼接参数,照样会被注入。只有内部使用参数化或严格类型校验时才有意义。
3. 白名单校验
对于排序字段、表名、列名这类不能参数化的位置,使用白名单映射:
$allowed = ['created_at', 'updated_at', 'id'];
$order = in_array($_GET['order'], $allowed, true) ? $_GET['order'] : 'id';
4. 最小权限数据库账号
应用账号只授予必要的 SELECT、INSERT、UPDATE、DELETE,禁止 DROP、FILE、INTO OUTFILE。这样即使注入成功,危害也被限制。
要避免的写法:字符串拼接、addslashes、mysql_real_escape_string 单独使用、把数字参数不加引号直接拼接。这些在宽字节、编码差异等场景下仍可能被绕过。
三、Linux 服务器安全加固清单
服务器是输入校验的最终执行环境。如果服务器本身被攻破,前面所有校验都失去意义。以下是一份可落地的加固清单:
账号与权限
- 禁用 root 远程 SSH 登录,使用普通用户 +
sudo。 - 使用 SSH 密钥认证,关闭密码登录。
- 删除或锁定不必要的系统账号,检查
uid 0的账号是否只有 root。 - 应用以独立低权限用户运行,不用 root 启动 Web 服务。
网络与服务
- 使用防火墙(iptables/nftables/ufw)只开放必要端口。
- 关闭不需要的服务:telnet、ftp、rpcbind 等。
- 数据库只监听
127.0.0.1或内网地址,不暴露公网。 - 配置 fail2ban 限制 SSH 和 Web 登录爆破。
系统与补丁
- 开启自动安全更新,或建立定期补丁流程。
- 内核参数加固:
net.ipv4.tcp_syncookies=1、kernel.randomize_va_space=2、禁止 IP 转发。 - 使用
chattr +i保护关键文件如/etc/passwd、/etc/shadow(需谨慎评估)。
日志与监控
- 开启 auditd 或至少集中收集
/var/log/auth.log、Web 访问日志。 - 监控异常进程、异常外连、
/tmp下可执行文件。 - 定期用
lynis、rkhunter做安全审计。
Web 层配合
- Nginx/Apache 限制请求体大小、超时时间、并发连接数。
- 上传目录禁止执行脚本。
- 设置安全的响应头:
X-Content-Type-Options、X-Frame-Options、Strict-Transport-Security。
四、三层边界如何分工
回到主题:输入校验的边界应该这样划分:
- 前端:提升用户体验,做即时反馈和基本格式校验,同时承担 DOM 型 XSS 的防御。但不承担安全可信边界。
- 后端:安全校验的核心。所有进入业务逻辑、数据库、文件系统的数据都必须在这里做类型、长度、范围、白名单校验;输出时按上下文编码。
- 网关/WAF:做粗粒度拦截和流量清洗,比如拦截明显 SQL 注入特征、限制频率、阻断恶意 IP。它是纵深防御的一层,但不能替代后端参数化查询和输出编码。
一个常见的错误是:后端认为 WAF 会拦,WAF 认为后端会校验,前端认为后端会处理。结果是三层都漏。正确的做法是:每一层都假设上一层可能被绕过,只做自己边界内能确定的事。前端不信任用户输入,后端不信任前端,网关不信任任何流量,服务器不信任应用。这样,输入校验才真正形成闭环。
未经允许不得转载:任鹏个人博客 » Web 安全中的输入校验边界:前端、后端与网关


朋友圈点赞图在线生成源码