常见安全风险
- JWT 被窃取:
- 风险:攻击者可能通过网络嗅探、XSS 攻击、CSRF 攻击等手段获取用户的 JWT,从而冒用用户身份访问受保护资源。
- 示例:在公共无线网络环境中,攻击者使用网络抓包工具获取用户传输的 JWT。
- JWT 被篡改:
- 风险:攻击者修改 JWT 中的声明(如用户角色、权限等信息),以获取超出其应有权限的访问。
- 示例:将普通用户角色在 JWT 中篡改为管理员角色。
- 密钥泄露:
- 风险:JWT 的签名验证依赖密钥,如果密钥泄露,攻击者可以伪造任意有效的 JWT。
- 示例:服务器配置文件被入侵,其中存储的 JWT 密钥被获取。
- 过期时间设置不当:
- 风险:如果 JWT 的过期时间设置过长,用户的会话长期有效,增加了 JWT 被窃取后造成危害的时间窗口;如果设置过短,用户频繁需要重新登录,影响用户体验。
应对策略及技术实现方案
- 防范 JWT 被窃取:
- 使用 HTTPS:
- 策略:通过 HTTPS 协议传输 JWT,对通信进行加密,防止网络嗅探获取 JWT。
- 实现:在服务器端配置 SSL/TLS 证书,例如在使用 Nginx 服务器时,配置
ssl_certificate
和 ssl_certificate_key
指令。
- 防止 XSS 攻击:
- 策略:对用户输入进行严格的过滤和转义,避免恶意脚本注入,从而防止攻击者通过 XSS 获取 JWT。
- 实现:在 Web 应用开发中,使用安全的库进行输入验证和转义,如在 Java 的 Spring 框架中,可以使用
Spring Security
的内置机制对输入进行净化。
- 防止 CSRF 攻击:
- 策略:使用 CSRF 防护机制,如在 JWT 验证的基础上,结合 CSRF 令牌(Token)。在每次请求时,除了验证 JWT,还验证 CSRF 令牌的有效性。
- 实现:在前端生成 CSRF 令牌并存储在用户会话中,每次表单提交或 AJAX 请求时,将 CSRF 令牌发送到服务器,服务器进行验证。例如在 Python 的 Django 框架中,
django.middleware.csrf.CsrfViewMiddleware
中间件自动处理 CSRF 防护。
- 防范 JWT 被篡改:
- 使用签名验证:
- 策略:在生成 JWT 时,使用密钥对其进行签名。服务器在接收到 JWT 时,使用相同的密钥验证签名的有效性。如果 JWT 被篡改,签名验证将失败。
- 实现:在 Java 中,使用
jjwt
库生成和验证 JWT 签名,示例代码如下:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
public class JwtUtil {
private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
public static String generateToken(String subject) {
return Jwts.builder()
.setSubject(subject)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public static Claims verifyToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
}
}
- 防范密钥泄露:
- 密钥管理:
- 策略:妥善保管密钥,避免在代码中明文存储。可以使用安全的密钥管理服务(KMS)。
- 实现:例如在 AWS 环境中,可以使用 AWS Key Management Service(KMS)来管理密钥。在应用程序中,通过调用 KMS API 获取密钥进行 JWT 签名和验证。
- 密钥定期更换:
- 策略:定期更换 JWT 签名密钥,降低密钥泄露造成的风险。
- 实现:制定密钥更换计划,例如每月更换一次密钥。在更换密钥时,需要确保新旧密钥在一段时间内并存,以处理仍在有效期内的旧 JWT。
- 合理设置过期时间:
- 动态调整过期时间:
- 策略:根据应用场景和安全需求动态设置 JWT 的过期时间。例如,对于高风险操作(如资金交易等),设置较短的过期时间;对于一般的浏览操作,设置相对较长的过期时间。
- 实现:在生成 JWT 时,根据业务逻辑设置
exp
(过期时间)声明。例如在 Node.js 中使用 jsonwebtoken
库:
const jwt = require('jsonwebtoken');
const secret = 'your - secret - key';
// 高风险操作,过期时间设为 5 分钟
const highRiskToken = jwt.sign({ data: 'user - data' }, secret, { expiresIn: '5m' });
// 一般操作,过期时间设为 1 小时
const normalToken = jwt.sign({ data: 'user - data' }, secret, { expiresIn: '1h' });
- 刷新令牌机制:
- 策略:引入刷新令牌(Refresh Token)机制,当 JWT 过期时,使用刷新令牌获取新的 JWT,而无需用户重新登录。
- 实现:用户登录成功后,除了返回 JWT,还返回一个刷新令牌。刷新令牌存储在服务器端(例如数据库),当 JWT 过期时,客户端携带刷新令牌请求新的 JWT。服务器验证刷新令牌的有效性后,生成新的 JWT 返回给客户端。例如在 Python 的 Flask 应用中,可以使用
flask - jwt - extended
扩展实现刷新令牌机制。