第七章:安全最佳实践¶
本章介绍 OAuth2/OIDC 的安全最佳实践。
传输安全¶
强制 HTTPS¶
# Keycloak 配置
KC_HOSTNAME: auth.example.com
KC_HOSTNAME_STRICT: "true"
KC_HTTPS_CERTIFICATE_FILE: /etc/ssl/certs/tls.crt
KC_HTTPS_CERTIFICATE_KEY_FILE: /etc/ssl/certs/tls.key
HSTS Header¶
Token 安全¶
短期令牌¶
# Keycloak 配置
accessTokenLifespan: 300 # 5分钟
ssoSessionMaxLifespan: 36000 # 10小时
ssoSessionIdleTimeout: 1800 # 30分钟
Refresh Token 安全¶
refreshTokenMaxReuse: 3 # 最多重用3次
revokeRefreshToken: true # 使用后撤销
offlineSessionMaxLifespan: 2592000 # 30天
Token 存储¶
// 不推荐:存储在 localStorage
localStorage.setItem('token', token); // 易受 XSS 攻击
// 推荐:存储在 HttpOnly Cookie
// 服务端设置
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000
});
PKCE 保护¶
// 始终使用 PKCE
const userManager = new UserManager({
authority: 'https://auth.example.com',
client_id: 'my-client',
redirect_uri: 'https://app.example.com/callback',
response_type: 'code',
scope: 'openid profile',
code_challenge_method: 'S256' // 启用 PKCE
});
State 和 Nonce¶
State 参数¶
import secrets
def login():
state = secrets.token_urlsafe(32)
session['oauth_state'] = state
auth_url = f"{auth_server}/authorize?client_id={client_id}&redirect_uri={redirect_uri}&state={state}"
return redirect(auth_url)
def callback(state, code):
if state != session.pop('oauth_state'):
raise ValueError("Invalid state")
# 继续处理
Nonce 参数¶
def login():
nonce = secrets.token_urlsafe(32)
session['oauth_nonce'] = nonce
auth_url = f"{auth_server}/authorize?...&nonce={nonce}"
return redirect(auth_url)
def verify_id_token(id_token, nonce):
payload = jwt.decode(id_token, ...)
if payload.get('nonce') != nonce:
raise ValueError("Invalid nonce")
CORS 配置¶
输入验证¶
Redirect URI 验证¶
ALLOWED_REDIRECT_URIS = [
"https://app.example.com/callback",
"https://admin.example.com/callback"
]
def validate_redirect_uri(redirect_uri):
if redirect_uri not in ALLOWED_REDIRECT_URIS:
raise ValueError("Invalid redirect URI")
Scope 验证¶
ALLOWED_SCOPES = ["openid", "profile", "email"]
def validate_scopes(scopes):
for scope in scopes:
if scope not in ALLOWED_SCOPES:
raise ValueError(f"Invalid scope: {scope}")
审计日志¶
import logging
logger = logging.getLogger('auth')
def log_auth_event(event_type, user_id, client_id, ip_address):
logger.info({
"event_type": event_type,
"user_id": user_id,
"client_id": client_id,
"ip_address": ip_address,
"timestamp": datetime.utcnow().isoformat()
})
# 使用
log_auth_event("login", user_id, client_id, request.remote_addr)
log_auth_event("token_issued", user_id, client_id, request.remote_addr)
log_auth_event("logout", user_id, client_id, request.remote_addr)
常见攻击防护¶
CSRF 防护¶
XSS 防护¶
from flask import escape
@app.route('/profile')
def profile():
name = escape(user.name) # 转义用户输入
return f"<h1>Hello, {name}</h1>"
Token 泄露检测¶
安全检查清单¶
- 强制 HTTPS
- 使用短期令牌
- 启用 PKCE
- 验证 State 和 Nonce
- 严格验证 Redirect URI
- 限制 Scope
- 记录审计日志
- 定期轮换密钥
- 启用 MFA
- 监控异常行为
小结¶
安全最佳实践要点:
- 传输安全:HTTPS、HSTS
- Token 安全:短期令牌、安全存储
- PKCE:防止授权码劫持
- 输入验证:Redirect URI、Scope
- 审计日志:记录关键事件
下一章我们将学习生产实践。