安全不是事后补丁,而是架构设计时的第一约束。
后端系统的安全性,往往在"出事后"才被重视。但真正成熟的安全体系,必须从设计阶段就开始构建——认证、授权、传输加密、密钥管理、审计合规,每一层都不能缺。
本文将从 OAuth2.1 出发,系统梳理现代后端安全架构的完整实践路径。
目录
- 1. 引言:安全是架构设计的一部分
- 2. OAuth 2.1 与 OpenID Connect
- 3. JWT 安全实践
- 4. 认证架构设计:自建 vs 托管
- 5. 授权模型对比:RBAC vs ABAC vs ReBAC
- 6. API 安全防护
- 7. Web 安全基础:OWASP Top 10 防御要点
- 8. 密钥管理
- 9. 零信任架构(Zero Trust)
- 10. 安全审计与合规
- 11. 总结与安全 Checklist
- 12. 延伸阅读
1. 引言:安全是架构设计的一部分
很多团队把安全当成"上线前补一补"的工作。这是一个危险的错觉。
安全必须从第一天就作为架构的非功能性约束:
- 认证:证明"你是谁"
- 授权:决定"你能做什么"
- 加密:保证"数据不被窃取"
- 审计:回答"发生了什么"
- 合规:满足"法规要求"
安全不是功能,是系统的底色。
2. OAuth 2.1 与 OpenID Connect
OAuth 2.1 是对 OAuth 2.0 的整理与安全强化版本,主要变化:
- 强制使用 PKCE(Proof Key for Code Exchange),防御授权码拦截
- 废弃隐式授权(Implicit Grant)和资源所有者密码模式
- 强制 Refresh Token Rotation,防御 Token 重放
- 要求 sender-constrained tokens(如 DPoP 或 mTLS)
OpenID Connect(OIDC)在 OAuth2.1 之上增加了 身份认证层(id_token + UserInfo Endpoint)。
2.1 授权码流程与 PKCE 扩展
PKCE 流程的关键步骤:
- 客户端生成随机
code_verifier(43–128 字符) - 计算
code_challenge = BASE64URL(SHA256(code_verifier)) - 授权请求携带
code_challenge - Token 请求携带
code_verifier,服务端校验
2.2 OAuth2.1 完整流程图
User Browser App (Client) Auth Server Resource Server
│ │ │ │ │
│──Login────▶│ │ │ │
│ │─redirect─────▶│ │ │
│ │ │──/authorize─────▶│ │
│ │ │ (code_challenge) │ │
│ │◀─login page───│◀─────────────────│ │
│─credentials▶ │ │ │
│ │──────────────────────────────────▶ │
│ │◀─redirect?code=X─────────────────│ │
│ │──code────────▶│ │ │
│ │ │──/token─────────▶│ │
│ │ │ (code + verifier) │
│ │ │◀─access_token────│ │
│ │ │ │ │
│ │ │─────GET /api (Bearer token)──────────▶│
│ │ │◀───────200 OK────────────────────────│
2.3 Token 生命周期管理
| Token | 推荐有效期 | 刷新策略 |
|---|---|---|
| Access Token | 5–15 分钟 | 不可刷新,过期换发 |
| Refresh Token | 7–30 天 | Rotation,每次换发新 Token |
| ID Token | 同 Access Token | 一次性使用 |
3. JWT 安全实践
3.1 签名算法选择
| 算法 | 类型 | 适用场景 | 性能 |
|---|---|---|---|
| RS256 | RSA 非对称 | 通用场景,公钥可公开 | 中 |
| ES256 | ECDSA 非对称 | 对性能/带宽敏感场景 | 高 |
| HS256 | HMAC 对称 | 仅限单服务内部 | 高 |
禁止使用:alg: none、HS256(多服务场景)。
3.2 Token 结构
{
"header": { "alg": "ES256", "typ": "JWT", "kid": "key-2024-11" },
"payload": {
"sub": "user-123",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1733000000,
"iat": 1732999100,
"scope": "read:orders write:orders",
"jti": "unique-token-id"
}
}
必要字段:sub、iss、aud、exp、iat、jti。
3.3 常见攻击与防御
| 攻击 | 描述 | 防御 |
|---|---|---|
| None 算法攻击 | 把 alg 改为 none,绕过签名 | 服务端强制校验算法白名单 |
| 算法混淆 | 把 RS256 换成 HS256 | 按 kid 严格匹配算法 |
| Token 重放 | 截获 Token 重复使用 | 短有效期 + jti 黑名单 |
| JWK 注入 | 攻击者指定自己的公钥 | 不信任 header 中的 jku/x5u |
| 跨服务滥用 | A 服务 Token 用于 B 服务 | 强制 aud 校验 |
4. 认证架构设计:自建 vs 托管
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Auth0 | 成熟、文档好、合规 | 成本高、数据在第三方 | SaaS 产品 |
| Keycloak | 开源、可私有化 | 运维复杂 | 企业内网 |
| Cognito | 与 AWS 深度集成 | 锁定 AWS、UX 一般 | AWS 全家桶团队 |
| 自建(ORY Hydra / 自研) | 完全可控 | 安全投入大 | 有专职安全团队 |
推荐:中小团队直接用托管服务,把安全精力聚焦在业务层。
5. 授权模型对比:RBAC vs ABAC vs ReBAC
| 模型 | 全称 | 核心思想 | 适用场景 |
|---|---|---|---|
| RBAC | Role-Based Access Control | 用户 → 角色 → 权限 | 简单权限模型 |
| ABAC | Attribute-Based Access Control | 基于属性的策略规则 | 复杂业务规则 |
| ReBAC | Relationship-Based Access Control | 基于实体关系(如 Google Zanzibar) | 多租户、资源级权限 |
RBAC 数据模型示例:
CREATE TABLE roles (
id UUID PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL
);
CREATE TABLE permissions (
id UUID PRIMARY KEY,
resource VARCHAR(50) NOT NULL,
action VARCHAR(20) NOT NULL -- read, write, delete
);
CREATE TABLE role_permissions (
role_id UUID REFERENCES roles(id),
permission_id UUID REFERENCES permissions(id),
PRIMARY KEY (role_id, permission_id)
);
CREATE TABLE user_roles (
user_id UUID REFERENCES users(id),
role_id UUID REFERENCES roles(id),
PRIMARY KEY (user_id, role_id)
);
ABAC 策略示例(OPA Rego):
package authz
default allow = false
allow {
input.user.department == input.resource.department
input.user.level >= 3
input.action == "read"
}
6. API 安全防护
6.1 限流
| 算法 | 描述 | 适用场景 |
|---|---|---|
| 令牌桶 | 固定速率填充,突发可消耗积攒令牌 | 通用 API 限流 |
| 漏桶 | 固定速率处理,超出的丢弃 | 平滑流量 |
| 滑动窗口 | 基于时间窗口的计数 | 简单计数限流 |
限流必须分层:IP 层 → 用户层 → API 层 → 后端服务层。
6.2 输入校验与注入防御
SQL 注入防御三原则:
- 永远使用参数化查询(Prepared Statement)
- 使用 ORM,避免拼接 SQL
- 数据库账户最小权限
// BAD: 字符串拼接
db.Query("SELECT * FROM users WHERE id = " + id)
// GOOD: 参数化查询
db.Query("SELECT * FROM users WHERE id = $1", id)
6.3 CORS 配置
Access-Control-Allow-Origin: https://app.example.com # 不要用 *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
7. Web 安全基础:OWASP Top 10 防御要点
| 漏洞 | 防御要点 |
|---|---|
| XSS | 输出编码、CSP Header、HttpOnly Cookie |
| CSRF | SameSite Cookie、CSRF Token |
| SSRF | 禁止用户输入直连内网,URL 白名单 |
| 路径遍历 | 路径规范化,禁止 .. |
| 反序列化 | 禁用不安全的反序列化库 |
| 敏感数据泄露 | 加密存储、日志脱敏 |
推荐 HTTP 安全 Header:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
8. 密钥管理
永远不要把密钥放在代码、环境变量、Git 仓库中。
HashiCorp Vault 配置示例:
# 启用 KV Secrets Engine
vault secrets enable -path=secret kv-v2
# 写入密钥
vault kv put secret/myapp/database \
username="db_user" \
password="super-secret"
# 配置数据库动态凭证
vault secrets enable database
vault write database/config/mydb \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db:5432/myapp" \
allowed_roles="app-role"
vault write database/roles/app-role \
db_name=mydb \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl="1h" \
max_ttl="24h"
密钥轮换策略:
- 数据库密码:每 90 天轮换,或使用动态凭证
- JWT 签名密钥:每 30 天轮换,保留 2 个版本
- API Key:每 6 个月轮换,支持双 Key 平滑切换
9. 零信任架构(Zero Trust)
零信任的核心原则:永不信任,始终验证(Never Trust, Always Verify)。
关键组件:
| 组件 | 技术 | 作用 |
|---|---|---|
| mTLS | Istio / Linkerd / cert-manager | 服务间双向 TLS 认证 |
| SPIFFE/SPIRE | 工作负载身份框架 | 标准化服务身份与凭证分发 |
| Service Mesh | Istio / Linkerd | 流量策略 + 加密 + 认证 |
| Policy Engine | OPA / Kyverno | 授权策略执行 |
mTLS 流程:
Service A ──(TLS Hello)──▶ Service B
Service A ◀──(Server Cert)── Service B
Service A ──(Client Cert)──▶ Service B
Service B ──(verify via CA)──▶ 允许请求
SPIFFE ID 格式:
spiffe://trust-domain.example.com/ns/production/sa/order-service
10. 安全审计与合规
日志审计必须记录:
- 所有认证事件(成功/失败)
- 权限变更操作
- 敏感数据访问(PII / 财务数据)
- 管理后台的所有操作
GDPR / 数据保护设计原则:
- 最小化收集:只收集业务必需数据
- 目的限制:数据只能用于声明的目的
- 存储期限:明确保留期限,到期自动删除
- 用户权利:支持访问、更正、删除、导出
- 加密:敏感字段静态加密 + 传输加密
日志脱敏示例:
logger.Info("user login",
"user_id", user.ID,
"ip", hashIP(clientIP), // 哈希而非明文
"email", maskEmail(email), // 脱敏
)
11. 总结与安全 Checklist
构建一个安全的后端系统,至少需要满足以下条件:
- OAuth2.1 + PKCE 用于用户认证,废弃隐式授权
- JWT 使用 RS256 或 ES256,强制校验
iss、aud、exp - Access Token 短有效期,Refresh Token Rotation
- RBAC/ABAC 授权模型,遵循最小权限原则
- 所有 API 限流,分层防御
- SQL 查询参数化,杜绝注入
- HTTPS 强制,安全 Header 完整
- 密钥使用 Vault 管理,定期轮换
- 服务间通信使用 mTLS
- 安全事件完整记录,敏感数据脱敏
- 满足 GDPR / 数据保护合规要求
一句话总结:安全是系统的底色,而不是功能。它必须在设计阶段就被嵌入到每一层架构之中。
12. 延伸阅读
- OAuth 2.1 Draft Specification
- OpenID Connect Core 1.0
- OWASP Top 10 (2021)
- Google BeyondCorp - Zero Trust Architecture
- SPIFFE/SPIRE Documentation
- HashiCorp Vault Best Practices
- NIST Zero Trust Architecture (SP 800-207)
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。