面试题答案
一键面试抵御中间人攻击
- 防御机制:使用HTTPS协议。HTTPS通过SSL/TLS加密传输数据,使得中间人无法窃听或篡改传输中的JWT。
- 技术实现思路:在服务器端配置SSL证书,常见的如在Nginx中,可以通过以下配置启用HTTPS:
server {
listen 443 ssl;
server_name your_domain.com;
ssl_certificate /path/to/your_cert.pem;
ssl_certificate_key /path/to/your_key.pem;
...
}
在应用程序中,确保所有与客户端的通信都使用HTTPS协议,无论是获取JWT还是后续使用JWT进行身份验证的请求。
抵御重放攻击
- 防御机制:
- 添加JWT过期时间:设置较短的过期时间(exp),这样即使JWT被窃取,攻击者在过期后也无法使用。
- 使用一次性令牌:在JWT中添加一个唯一标识符(如jti),服务器端维护一个已使用过的jti列表,每次验证JWT时检查jti是否已被使用。
- 技术实现思路:
- 添加JWT过期时间:在生成JWT时,设置
exp
字段,例如在Python的PyJWT库中:
- 添加JWT过期时间:在生成JWT时,设置
import jwt
import datetime
payload = {
'user_id': 123,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes = 30)
}
token = jwt.encode(payload, 'your_secret_key', algorithm='HS256')
- **使用一次性令牌**:在生成JWT时,添加`jti`字段:
import uuid
import jwt
jti = str(uuid.uuid4())
payload = {
'user_id': 123,
'jti': jti
}
token = jwt.encode(payload, 'your_secret_key', algorithm='HS256')
在验证JWT时,检查jti
是否在已使用列表中,例如在一个简单的Python Flask应用中:
used_jti = set()
@app.route('/protected', methods=['GET'])
def protected():
token = request.headers.get('Authorization')
if not token:
return 'Token is missing', 401
try:
data = jwt.decode(token, 'your_secret_key', algorithms=['HS256'])
jti = data.get('jti')
if jti in used_jti:
return 'Replay attack detected', 401
used_jti.add(jti)
return 'Access granted'
except jwt.ExpiredSignatureError:
return 'Token has expired', 401
except jwt.InvalidTokenError:
return 'Invalid token', 401
抵御令牌泄露
- 防御机制:
- 强密钥管理:使用足够长度和复杂度的密钥来签名JWT,并且妥善保管密钥,避免泄露。
- 限制JWT使用范围:JWT应仅在必要的API端点使用,并且对不同的操作授予不同权限的JWT。
- 刷新令牌机制:使用刷新令牌来定期更新访问令牌,减少访问令牌暴露时间。
- 技术实现思路:
- 强密钥管理:生成一个足够长且复杂的密钥,例如使用
openssl rand -base64 32
生成一个32字节的Base64编码密钥。在代码中使用该密钥进行JWT签名和验证。 - 限制JWT使用范围:在API中间件中,根据JWT中的权限信息,限制对特定端点的访问。例如在Node.js的Express应用中:
- 强密钥管理:生成一个足够长且复杂的密钥,例如使用
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
app.get('/admin/endpoint', (req, res) => {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Token is missing');
try {
const decoded = jwt.verify(token, 'your_secret_key');
if (!decoded.isAdmin) {
return res.status(403).send('Access denied');
}
res.send('Admin access granted');
} catch (err) {
res.status(401).send('Invalid token');
}
});
- **刷新令牌机制**:当访问令牌过期时,客户端使用刷新令牌获取新的访问令牌。在服务器端,验证刷新令牌的有效性,并生成新的访问令牌。例如在Java的Spring Boot应用中:
// 生成刷新令牌
String refreshToken = Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS256, REFRESH_SECRET)
.compact();
// 使用刷新令牌获取新的访问令牌
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestBody RefreshTokenRequest request) {
Claims claims = Jwts.parser()
.setSigningKey(REFRESH_SECRET)
.parseClaimsJws(request.getRefreshToken())
.getBody();
String username = claims.getSubject();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
String token = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}