面试题答案
一键面试潜在安全风险分析
- JWT密钥管理风险
- 密钥泄露风险:如果JWT密钥被泄露,恶意用户可以使用该密钥伪造合法的JWT,从而访问所有版本的API。例如,密钥存储在不安全的配置文件中,或在开发、测试环境中被误公开。
- 多版本密钥管理复杂性:当存在多个API版本时,可能需要管理多个JWT密钥。若密钥管理不善,可能导致不同版本使用相同密钥,增加风险;或者密钥更新不及时,旧密钥仍可用于访问新版本API。
- JWT被盗用风险
- 中间人攻击:攻击者可能在网络传输过程中截获JWT。例如,在不安全的公共Wi-Fi环境下,用户发送包含JWT的请求,攻击者通过网络嗅探获取JWT,然后使用该JWT进行非法访问。
- Cookie劫持:如果JWT存储在浏览器的Cookie中,攻击者可以通过XSS(跨站脚本攻击)等手段获取用户的Cookie,进而盗用JWT。
防范措施和技术手段
- JWT密钥管理防范措施
- 安全存储密钥:使用安全的密钥管理服务(如HashiCorp Vault、AWS Secrets Manager等)来存储JWT密钥。这些服务提供加密存储、访问控制等功能,确保密钥的安全性。
- 版本化密钥管理:为每个API版本分配独立的JWT密钥,并记录密钥的使用版本。在密钥更新时,确保及时通知相关服务和客户端,并逐步淘汰旧密钥。
- 防止JWT被盗用防范措施
- 传输安全:使用HTTPS协议进行通信,对数据传输进行加密,防止中间人攻击截获JWT。
- JWT存储安全:避免将JWT存储在浏览器的Cookie中,而是使用HttpOnly和Secure属性设置Cookie,防止XSS攻击获取Cookie。或者将JWT存储在内存中,如在单页应用中使用LocalStorage时,结合加密和严格的访问控制。
代码层面实现
- JWT密钥管理代码实现(以Java Spring Boot为例)
- 使用Spring Security和JJWT库:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
@Component
public class JwtUtil {
@Value("${jwt.secret}")
private String secret;
public String generateToken(String subject) {
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
Date now = new Date();
Date expiration = new Date(now.getTime() + 10 * 60 * 1000); // 10分钟过期
return Jwts.builder()
.setSubject(subject)
.setIssuedAt(now)
.setExpiration(expiration)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public boolean validateToken(String token) {
try {
SecretKey key = Keys.hmacShaKeyFor(secret.getBytes());
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
Date expiration = claims.getExpiration();
return expiration.after(new Date());
} catch (Exception e) {
return false;
}
}
}
在配置文件中配置不同版本的密钥:
jwt.version1.secret=your-secret-key-for-version1
jwt.version2.secret=your-secret-key-for-version2
- 防止JWT被盗用代码实现(以Node.js Express为例)
- 使用jsonwebtoken库和Express:
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const secret = 'your-secret-key';
// 生成JWT
app.post('/login', (req, res) => {
const user = { username: req.body.username };
const token = jwt.sign(user, secret, { expiresIn: '1h' });
res.cookie('jwt', token, { httpOnly: true, secure: true });
res.send('Login successful');
});
// 验证JWT
app.get('/protected', (req, res) => {
const token = req.cookies.jwt;
if (!token) {
return res.status(401).send('Access denied. No token provided.');
}
try {
const decoded = jwt.verify(token, secret);
res.send(decoded);
} catch (err) {
res.status(400).send('Invalid token');
}
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
上述代码通过设置httpOnly
和secure
属性确保JWT在Cookie中的安全性,并在请求时验证JWT的有效性。