4. 应用边界防护:WAF部署、API网关安全、输入输出验证
应用层是攻击者最喜欢的地方。为什么?因为这里直接面对用户,攻击面最大。我见过太多公司把安全预算全砸在网络防火墙上,结果应用层漏洞百出,被SQL注入打得满地找牙。今天咱们就聊聊怎么把应用边界真正守好。
4.1 Web应用防火墙(WAF)部署
WAF这东西,说白了就是给Web应用戴个头盔。它工作在应用层(OSI第7层),能看懂HTTP请求里的猫腻。我习惯把WAF部署在反向代理的位置,所有流量先过WAF,再到后端应用。
核心价值:WAF能拦截SQL注入、XSS、CSRF、文件包含等OWASP Top 10攻击。但它不是万能药——它只能防已知攻击模式,业务逻辑漏洞它管不了。
4.1.1 部署模式对比
| 模式 | 延迟 | 安全性 | 运维成本 | 适用场景 |
|---|---|---|---|---|
| 反向代理(串联) | 低(1-3ms) | 高 | 中 | 对外Web服务 |
| 透明代理(桥接) | 极低(<1ms) | 中 | 低 | 内网流量审计 |
| 云WAF(DNS引流) | 中(5-15ms) | 高 | 低 | 多云/混合云 |
| API网关集成 | 低(2-5ms) | 高 | 高 | 微服务架构 |
我个人推荐生产环境用反向代理模式。云WAF也不错,但要注意DNS解析的TTL设置,别切流量时把业务搞断了。
4.1.2 规则配置要点
WAF的规则集不是越多越好。我曾经见过一个团队开了3000多条规则,结果正常业务请求被误拦了40%。你想想看,这谁受得了?
- 先白后黑:先放行业务白名单,再叠加黑名单规则
- 分级启用:核心API用严格模式,静态资源用宽松模式
- 定期调优:每周至少review一次误报日志
- 应急开关:保留一键关闭WAF的能力,防止误拦影响业务
避坑指南:我曾经遇到一个客户,WAF把正常用户登录请求当成暴力破解给拦了。查了半天,发现是规则里把"连续5次POST到/login"判定为攻击。但他们的前端框架每次登录都会发5个POST请求做预检。嗯,这就是典型的规则没调好。
4.2 API网关安全
微服务架构下,API网关就是整个系统的守门员。它不只是做路由转发,更要承担安全管控的职责。我习惯把API网关看作"安全策略执行点"。
4.2.1 安全功能矩阵
| 功能 | 说明 | 我建议的配置 |
|---|---|---|
| 身份认证 | OAuth2/JWT验证 | 强制所有API必须携带有效Token |
| 速率限制 | 防止滥用和DDoS | 按用户/按IP分级限流 |
| 请求校验 | Schema验证、参数类型检查 | 拒绝格式不符的请求 |
| 敏感数据脱敏 | 响应中替换手机号、身份证等 | 在网关层统一脱敏 |
| 审计日志 | 记录所有API调用 | 保留至少90天 |
4.2.2 认证与授权分离
很多团队把认证和授权混在一起做,结果权限模型一团糟。我的做法是:API网关只做认证(你是谁),业务服务做授权(你能干什么)。
// 网关层:验证JWT签名和有效期
function authenticate(token) {
try {
const decoded = jwt.verify(token, SECRET_KEY);
// 只验证签名和过期时间
// 不检查具体权限
return decoded;
} catch (err) {
throw new UnauthorizedError('Token无效');
}
}
// 业务服务:检查具体权限
function authorize(user, resource, action) {
// 根据用户角色和资源权限矩阵判断
if (!permissionService.check(user.role, resource, action)) {
throw new ForbiddenError('权限不足');
}
}
经验之谈:我在做金融项目时,API网关还承担了"敏感操作二次确认"的功能。比如转账超过5万,网关会拦截请求并返回一个确认码,用户必须通过短信验证后才能继续。这种逻辑放在网关层,所有业务服务都能复用。
4.3 输入输出验证
输入输出验证是应用安全最后一道防线。WAF和API网关可能漏掉一些攻击,但严格的输入输出验证能兜底。我把它叫做"安全底线"。
4.3.1 输入验证策略
记住一个原则:信任什么?什么都不信任。所有来自客户端的数据都是不可信的,包括HTTP头、Cookie、请求体、URL参数。
- 白名单验证:只允许预期的字符和格式
- 长度限制:每个字段都要有最大长度
- 类型强制:数字就转成数字类型,字符串就转义
- 编码统一:全部用UTF-8,防止编码绕过
// 输入验证示例:用户注册
function validateRegistrationInput(data) {
const errors = [];
// 用户名:只允许字母数字和下划线,长度3-20
if (!/^[a-zA-Z0-9_]{3,20}$/.test(data.username)) {
errors.push('用户名格式不正确');
}
// 邮箱:标准邮箱格式
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push('邮箱格式不正确');
}
// 年龄:必须是数字,范围0-150
const age = parseInt(data.age, 10);
if (isNaN(age) || age < 0 || age > 150) {
errors.push('年龄必须在0-150之间');
}
return errors;
}
4.3.2 输出编码
输出编码是防XSS的关键。很多开发者只在输入时做过滤,但输出时忘了编码。你想想看,攻击数据已经进了数据库,输出时不编码,等于把炸弹送到用户面前。
| 输出场景 | 编码方式 | 示例 |
|---|---|---|
| HTML上下文 | HTML实体编码 | <script> → <script> |
| JavaScript上下文 | Unicode转义 | " → \u0022 |
| URL上下文 | URL编码 | 空格 → %20 |
| CSS上下文 | CSS转义 | # → \# |
我曾经踩过的坑:有个项目在输出用户评论时,只做了HTML标签过滤,没做JavaScript编码。结果攻击者用<img src=x onerror=alert(1)>,虽然img标签被过滤了,但onerror属性还在。嗯,从那以后我坚持用成熟的模板引擎(如React的JSX、Vue的模板语法),它们自带输出编码。
4.4 三层防护体系
把WAF、API网关、输入输出验证组合起来,就形成了应用边界的三层防护。我习惯用下面这张图来跟团队讲清楚这个体系。
这三层不是互相替代的关系,而是互补。WAF挡不住业务逻辑漏洞,API网关管不了输出编码,输入输出验证防不了DDoS。只有三层都做好,应用边界才算真正安全。
核心要点:
- WAF部署选反向代理模式,规则集要精不要多
- API网关做认证,业务服务做授权,职责分离
- 输入验证用白名单,输出编码按上下文
- 三层防护叠加,形成纵深防御