微服务拆分精讲 / 第 14 章:安全架构
第 14 章:安全架构
微服务的攻击面远大于单体应用。零信任不是可选项,而是必选项。
14.1 微服务安全挑战
14.1.1 攻击面分析
单体应用攻击面: 微服务攻击面:
┌──────────┐ ┌────┐ ┌────┐ ┌────┐
│ │ │ GW │ │ S1 │ │ S2 │
│ 单体应用 │ └─┬──┘ └─┬──┘ └─┬──┘
│ │ │ │ │
│ 1 个入口│ ┌─┴──────┴──────┴──┐
│ 1 个数据库│ │ 消息队列 API网关 │
└──────────┘ │ 服务注册 配置中心 │
└───────┬──────────┘
│
┌───────┴──────────┐
│ K8s Docker │
│ CI/CD 监控平台 │
└──────────────────┘
攻击面:1-2 个 攻击面:20+ 个
14.1.2 安全威胁清单
| 威胁 | 说明 | 影响 |
|---|
| 服务间伪造 | 恶意服务冒充合法服务 | 数据泄露、篡改 |
| 中间人攻击 | 拦截服务间通信 | 数据窃取 |
| 未授权访问 | 越权调用其他服务的 API | 数据泄露 |
| API 滥用 | 恶意客户端大量调用 API | 服务不可用 |
| 配置泄露 | 敏感配置信息暴露 | 系统被入侵 |
| 供应链攻击 | 依赖的第三方库有漏洞 | 系统被入侵 |
| 容器逃逸 | 从容器突破到宿主机 | 整个集群被入侵 |
14.2 零信任安全模型
14.2.1 核心原则
传统安全模型: 零信任模型:
┌───────────────────┐ ┌───────────────────┐
│ 内部网络(信任) │ │ │
│ │ │ 每次请求都验证 │
│ 服务A ←→ 服务B │ │ │
│ (不验证) │ │ 服务A ──mTLS──▶ 服务B
│ │ │ (双向认证) │
│ 外部网络(不信任) │ │ │
│ ✗ │ │ 不分内外网 │
└───────────────────┘ └───────────────────┘
"永远不要信任,始终要验证"
(Never Trust, Always Verify)
14.2.2 零信任实施层次
| 层次 | 措施 | 说明 |
|---|
| 网络层 | mTLS、网络策略 | 服务间通信加密 + 授权 |
| 身份层 | 服务身份(SPIFFE) | 每个服务有唯一身份 |
| 认证层 | JWT/OAuth2 | 用户和客户端认证 |
| 授权层 | RBAC/ABAC | 细粒度权限控制 |
| 数据层 | 加密存储、脱敏 | 敏感数据保护 |
| 审计层 | 访问日志、审计追踪 | 所有操作可追溯 |
14.3 mTLS(双向 TLS)
14.3.1 单向 TLS vs 双向 TLS
单向 TLS (HTTPS):
┌──────────┐ ┌──────────┐
│ 客户端 │ │ 服务端 │
│ │──验证───▶│ 有证书 │
│ │ 服务端 │ │
│ │ 证书 │ │
│ │◀─加密通信─│ │
└──────────┘ └──────────┘
(客户端验证服务端,服务端不验证客户端)
双向 TLS (mTLS):
┌──────────┐ ┌──────────┐
│ 服务 A │ │ 服务 B │
│ 有证书 │◀─互相验证─▶│ 有证书 │
│ │ │ │
│ │◀─加密通信─▶│ │
└──────────┘ └──────────┘
(双方互相验证证书)
14.3.2 Istio mTLS 配置
# 命名空间级别启用 STRICT mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # STRICT: 强制 mTLS
# PERMISSIVE: 允许明文和 mTLS 共存
# DISABLE: 禁用 mTLS
# 验证 mTLS 是否生效
# 检查 PeerAuthentication
kubectl get peerauthentication --all-namespaces
# 查看服务间通信是否使用 mTLS
istioctl x describe pod <pod-name> -n production
14.4 JWT 认证
14.4.1 JWT 结构
JWT (JSON Web Token) 结构:
Header.Payload.Signature
┌──────────────────────────────────────────────────────────┐
│ Header (头部) │
│ { │
│ "alg": "RS256", // 签名算法 │
│ "typ": "JWT", │
│ "kid": "key-2026" // 密钥 ID(支持密钥轮换) │
│ } │
├──────────────────────────────────────────────────────────┤
│ Payload (载荷) │
│ { │
│ "sub": "user-001", // 用户 ID │
│ "name": "张三", │
│ "roles": ["admin"], // 角色 │
│ "permissions": ["order:read","order:write"], │
│ "iss": "auth.example.com", // 签发者 │
│ "exp": 1715500000, // 过期时间 │
│ "iat": 1715400000 // 签发时间 │
│ } │
├──────────────────────────────────────────────────────────┤
│ Signature (签名) │
│ RSASHA256(base64(header) + "." + base64(payload), key) │
└──────────────────────────────────────────────────────────┘
14.4.2 JWT 认证流程
┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 客户端 │ │ API 网关 │ │ 认证服务 │ │ 业务服务 │
└───┬────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. 登录请求 │ │ │
│ (用户名/密码)│ │ │
│─────────────▶│ │ │
│ │ 2. 转发 │ │
│ │─────────────▶│ │
│ │ │ │
│ │ 3. 返回 JWT │ │
│ │◀─────────────│ │
│ 4. JWT │ │ │
│◀─────────────│ │ │
│ │ │ │
│ 5. 请求 + JWT│ │ │
│─────────────▶│ │ │
│ │ 6. 验证 JWT │ │
│ │ (本地验证 │ │
│ │ 签名+过期) │ │
│ │ │ │
│ │ 7. 转发请求 │ │
│ │ (附加用户信息)│ │
│ │─────────────────────────▶ │
│ │ │ │
│ │ 8. 响应 │ │
│ │◀──────────────────────────│
│ 9. 响应 │ │ │
│◀─────────────│ │ │
14.4.3 JWT 安全最佳实践
| 实践 | 说明 |
|---|
| 使用 RS256 而非 HS256 | 非对称签名,网关只需公钥 |
| 设置合理过期时间 | Access Token 15-30 分钟 |
| 使用 Refresh Token | 用长期 Refresh Token 获取新 Access Token |
| 存储在 HttpOnly Cookie | 防止 XSS 攻击窃取 |
| 密钥轮换 | 定期更换签名密钥(kid 支持多版本) |
| 不要在 JWT 中放敏感信息 | JWT 只是 Base64 编码,不是加密 |
14.5 OAuth2 / OpenID Connect
14.5.1 OAuth2 授权流程
OAuth2 授权码流程 (Authorization Code Flow):
┌────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户 │ │ 客户端 │ │ 授权服务 │ │ 资源服务 │
└───┬────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │ │
│ 1. 点击登录 │ │ │
│─────────────▶│ │ │
│ │ │ │
│ │ 2. 重定向到授权页 │
│◀─────────────│─────────────▶│ │
│ │ │ │
│ 3. 用户授权 │ │ │
│─────────────────────────────▶ │
│ │ │ │
│ │ 4. 授权码 │ │
│ │◀─────────────│ │
│ │ │ │
│ │ 5. 用授权码换 Token │
│ │─────────────▶│ │
│ │ │ │
│ │ 6. Access Token + Refresh Token
│ │◀─────────────│ │
│ │ │ │
│ │ 7. 请求资源 + Token │
│ │─────────────────────────────▶
│ │ │ │
│ │ 8. 资源响应 │ │
│ │◀─────────────────────────────│
14.5.2 OAuth2 + JWT 组合
| 令牌类型 | 用途 | 有效期 | 存储位置 |
|---|
| Access Token | 访问 API | 15-30 分钟 | 内存/HttpOnly Cookie |
| Refresh Token | 获取新 Access Token | 7-30 天 | 安全存储(数据库) |
| ID Token | 用户身份信息 (OIDC) | 同 Access Token | 客户端 |
14.6 授权模型
14.6.1 RBAC(基于角色的访问控制)
RBAC 模型:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户 │───▶│ 角色 │───▶│ 权限 │
│ │ │ │ │ │
│ 张三 │ │ Admin │ │ order:* │
│ 李四 │ │ Viewer │ │ order:read│
└──────────┘ └──────────┘ └──────────┘
用户-角色映射:张三 → Admin, 李四 → Viewer
角色-权限映射:Admin → order:*, order:read, order:write, order:delete
Viewer → order:read
14.6.2 ABAC(基于属性的访问控制)
# 授权策略示例
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: order-service-policy
spec:
selector:
matchLabels:
app: order-service
rules:
# 订单服务只允许来自 API 网关的请求
- from:
- source:
principals: ["cluster.local/ns/production/sa/api-gateway"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/v1/orders/*"]
# 只允许订单服务自己访问内部 API
- from:
- source:
principals: ["cluster.local/ns/production/sa/order-service"]
to:
- operation:
paths: ["/internal/*"]
14.7 API 安全
14.7.1 API 安全清单
| 层次 | 措施 | 说明 |
|---|
| 传输层 | HTTPS/TLS | 加密传输 |
| 认证层 | JWT/OAuth2 | 身份验证 |
| 授权层 | RBAC/ABAC | 权限控制 |
| 输入校验 | 参数验证 | 防注入攻击 |
| 限流 | Rate Limiting | 防滥用 |
| 审计 | 访问日志 | 可追溯 |
| CORS | 跨域策略 | 防未授权访问 |
14.7.2 常见 API 安全漏洞
OWASP API Security Top 10 (2023):
1. BOLA (Broken Object Level Authorization)
→ 对象级别授权失效
→ 修复:验证用户有权限访问该对象
2. BFLA (Broken Function Level Authorization)
→ 功能级别授权失效
→ 修复:每个 API 端点都有权限检查
3. BPML (Broken Object Property Level Authorization)
→ 属性级别授权失效
→ 修复:限制可读写的字段
4. Unrestricted Resource Consumption
→ 资源消耗无限制
→ 修复:限流、分页限制、请求体大小限制
5. Broken Function Level Authorization
→ 未限制的访问控制
→ 修复:实施最小权限原则
14.8 Secrets 管理
14.8.1 方案对比
| 方案 | 说明 | 适用场景 |
|---|
| K8s Secrets | 原生 Secret(Base64 编码) | 简单场景 |
| HashiCorp Vault | 企业级 Secrets 管理 | 生产环境首选 |
| AWS Secrets Manager | AWS 原生 | AWS 环境 |
| Sealed Secrets | 加密的 K8s Secrets | GitOps 场景 |
| External Secrets Operator | 从外部系统同步 Secret | 多云环境 |
14.8.2 HashiCorp Vault 集成
Vault 工作流:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 服务 │ │ Vault │ │ 数据库 │
│ │ │ │ │ │
│ 1. 请求 │───▶│ 2. 验证 │───▶│ │
│ 凭证 │ │ 身份 │ │ │
│ │ │ │ │ │
│ 4. 使用 │◀───│ 3. 签发 │ │ │
│ 凭证 │ │ 临时凭证│ │ │
│ │ │ (TTL) │ │ │
└──────────┘ └──────────┘ └──────────┘
特点:
• 动态秘钥(临时凭证,自动过期)
• 自动轮换
• 审计日志
• 加密存储
14.9 容器安全
14.9.1 容器安全最佳实践
| 实践 | 说明 |
|---|
| 最小基础镜像 | 使用 distroless 或 Alpine |
| 非 root 运行 | USER 1000:1000 |
| 只读文件系统 | readOnlyRootFilesystem: true |
| 镜像扫描 | Trivy/Snyk 扫描漏洞 |
| 签名验证 | Cosign 签名验证镜像来源 |
| 网络策略 | 限制 Pod 间通信 |
| Pod Security Standards | 限制特权容器 |
# K8s SecurityContext
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: order-service
image: order-service:v2.1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
limits:
memory: "512Mi"
cpu: "500m"
⚠️ 注意事项
- JWT 不是万能的——适用于无状态认证,有状态场景考虑 Session
- 不要自己实现加密——使用成熟的加密库和标准算法
- 密钥管理是关键——泄露密钥等于泄露整个系统
- 安全左移——在开发阶段就开始安全检查(SAST/DAST)
- 定期安全审计——依赖漏洞扫描、渗透测试
📖 扩展阅读
- OWASP API Security Top 10 (owasp.org) — API 安全风险清单
- JWT Best Practices (auth0.com) — JWT 安全最佳实践
- HashiCorp Vault (vaultproject.io) — 企业级 Secrets 管理
- SPIFFE/SPIRE — 服务身份标准
- Zero Trust Networks — Evan Gilman, Doug Barth — O’Reilly 零信任架构
本章小结
| 安全层次 | 措施 | 工具/技术 |
|---|
| 网络安全 | mTLS、网络策略 | Istio, Calico |
| 认证 | JWT/OAuth2 | Keycloak, Auth0 |
| 授权 | RBAC/ABAC | Istio AuthorizationPolicy |
| 数据安全 | 加密存储、脱敏 | Vault, Sealed Secrets |
| 容器安全 | 最小权限、镜像扫描 | Trivy, Pod Security Standards |
| 审计 | 访问日志 | ELK, Falco |
📌 下一章:第 15 章:容器化与编排 — Docker、Kubernetes、服务编排策略。