API 安全实战:认证、授权与常见攻击防护
API 安全不是"加个 Token 就行"。本文从认证与授权的基础模型出发,讨论 JWT 与 Session 的选型、OAuth 2.0 的四种授权流程、以及 SQL 注入、XSS、CSRF、DDoS 等常见攻击的防御策略——适合正在搭建或审查 API 安全体系的后端开发者和架构师。
先说结论:API 安全不是”加个 Token 就行”
很多团队在 API 开发初期对安全的投入只有”加个 Token 验证”,等到上线后出现数据泄露、接口被刷、恶意爬取时才回头补课。但安全不是补丁,是设计阶段就该考虑的结构性问题。
本文从三个层面展开:认证与授权、常见攻击防护、安全配置清单。
1. 认证与授权
1.1 认证方式选型
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| JWT | 无状态,跨服务共享,不依赖存储 | 无法主动撤销,Payload 不过大 | 微服务、对外 API |
| Session | 可主动登出,服务端完全控制 | 需要集中存储,跨服务需共享 Session | 单体、管理系统 |
| API Key | 简单,适合机器间通信 | 粒度粗,只能识别应用不能识别用户 | 第三方集成、开放 API |
推荐实践:对外 API 用 JWT + 短期过期(15-60 分钟),配合 Refresh Token 实现无感续期。对内管理系统用 Session + Redis 存储,支持主动登出和权限回收。
1.2 OAuth 2.0 授权流程
如果你的 API 需要授权第三方访问用户数据,OAuth 2.0 是标准方案。四种授权流程对应不同场景:
Authorization Code(授权码) → 第三方应用访问用户数据(最常用)
Client Credentials(客户端凭证) → 服务间通信(无需用户授权)
Resource Owner Password(密码) → 第一方应用登录(不推荐,除非高度信任)
Implicit(隐式) → 已废弃,改用 PKCE
对于大多数场景,只需要 Authorization Code + PKCE 这一种流程就够了。
1.3 权限模型
API 级别的权限控制建议用 RBAC(基于角色的访问控制):
// 用户 → 角色 → 权限
{
"userId": 1,
"roles": ["admin", "editor"],
"permissions": ["post:create", "post:edit", "user:delete"]
}
中间件在每个需要鉴权的接口检查权限:
function requirePermission(perm: string) {
return (req, res, next) => {
if (!req.user.permissions.includes(perm)) {
return res.status(403).json({ error: 'forbidden' });
}
next();
};
}
2. 常见攻击与防御
2.1 SQL 注入
虽然 ORM 框架的普及使 SQL 注入减少,但在 ORDER BY 子句、动态表名、LIKE 查询中仍然存在。
防御:任何时候使用参数化查询,不拼接 SQL 字符串。
// ❌ 错误
const sql = `SELECT * FROM users WHERE name = '${name}'`;
// ✅ 正确
const sql = 'SELECT * FROM users WHERE name = $1';
2.2 XSS(跨站脚本攻击)
当 API 返回用户输入的内容(如评论、个人简介)时,如果未做转义,攻击者可以注入脚本。
防御:API 返回用户输入内容时做 HTML 转义,或使用 Content-Type: application/json 确保浏览器不会将响应解析为 HTML。
2.3 CSRF(跨站请求伪造)
攻击者诱导用户在已登录状态下访问恶意链接,利用用户的登录态执行非预期操作。
防御:使用 SameSite Cookie 属性(SameSite=Strict 或 SameSite=Lax),或要求每个写操作请求携带 CSRF Token。
2.4 限流与防刷
API 接口暴露后,没有限流保护就是敞开了让人刷。
分层限流策略:
网关层:按 IP 全局限流 → 100 req/min/IP
应用层:按用户 ID 细粒度限流 → 10 req/min/user(写操作)
核心接口:登录/注册/密码重置 → 5 req/min/IP
用 Redis 实现滑动窗口计数器:
async function rateLimit(key: string, limit: number, window: number): Promise<boolean> {
const current = await redis.incr(key);
if (current === 1) await redis.expire(key, window);
return current <= limit;
}
2.5 HTTPS
所有生产环境的 API 必须强制 HTTPS。在代码层面拒绝 HTTP 请求:
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(301, `https://${req.headers.host}${req.url}`);
}
3. 安全配置清单
| 类别 | 检查项 | 说明 |
|---|---|---|
| 传输层 | HTTPS 强制 | 301 跳转 + HSTS 头 |
| 认证 | Token 过期 | JWT 过期时间 ≤ 60 分钟 |
| 授权 | 接口级权限校验 | 每个接口检查当前用户是否有对应权限 |
| 输入 | 参数化查询 | 所有数据库操作禁止拼接 SQL |
| 输出 | HTML 转义 | 用户输入内容返回前做转义 |
| 请求 | 限流 | 分层限流(网关+应用+核心接口) |
| 请求 | CORS 白名单 | 只允许可信任的域名跨域访问 |
| 响应 | 安全头 | X-Frame-Options、X-Content-Type-Options、Referrer-Policy |
API 安全不是一次性投入——每次新增接口、每次升级依赖,都是重新审视安全配置的机会。 把安全清单纳入 Code Review 和 CI 检查,比等出了问题再补救成本低得多。
需要后端 API 开发或安全审计服务?联系我们,说清你的接口场景与规模,24 小时内回可行性。
相关阅读
- OpenAPI 文档最佳实践:从接口描述到可交付契约 —— API 规范与安全配置的配合
- REST vs GraphQL vs gRPC:后端 API 协议选型的决策框架 —— API 选型的上游决策
常见问题
JWT 和 Session 认证怎么选?
JWT 适合无状态、跨服务的场景(如微服务之间传递身份信息),但不支持服务端主动登出,Token 泄露后无法撤销。Session 适合单体或可以通过集中 Session 存储的服务,支持主动登出,但需要额外的存储层(Redis/DB)。建议:对外 API 用 JWT 配合短期过期时间(15-60 分钟),对内管理系统用 Session。
API 需要 HTTPS 吗?
需要。HTTPS 不仅加密传输内容防止中间人攻击,还提供了身份验证(确保你在和真正的服务器通信)和数据完整性(确保传输内容未被篡改)。没有 HTTPS 的 API,Token 和密码在网络传输中是明文,抓包就能看到。所有生产环境的 API 都应该强制 HTTPS,并在代码层面拒绝 HTTP 请求。
SQL 注入现在还有用吗?
有用。虽然 ORM 框架的普及减少了 SQL 注入的发生概率,但只要还存在原生 SQL 拼接(尤其是 ORDER BY 子句、动态表名、LIKE 查询),SQL 注入就没有消失。防御手段只有一条:永远不要拼接 SQL 字符串,任何时候都使用参数化查询或预编译语句。
API 的限流应该怎么做?
分层限流:① 网关层按 IP 和 API Key 做全局限流(如每 IP 每分钟 100 次);② 应用层按用户 ID 做更细粒度的限流(如每用户每分钟 10 次写操作);③ 核心接口做针对性限流(如登录接口每 IP 每分钟 5 次)。工具上可以用 Redis 的 INCR + EXPIRE 实现滑动窗口计数器,或用 Nginx 的 limit_req 模块做简单限流。