MST

星途 面试题库

面试题:安全认证之JWT在API文档生成工具中的安全风险与应对策略

假设你正在开发一个基于JWT的API文档生成工具,从安全角度分析,JWT可能存在哪些安全风险,针对每种风险,你会采取怎样的应对策略?请详细说明并结合实际代码示例(如在主流后端语言中的实现)。
25.4万 热度难度
后端开发安全认证

知识考点

AI 面试

面试题答案

一键面试

JWT安全风险及应对策略

  1. 密钥泄露风险
    • 风险:JWT的签名验证依赖于密钥,如果密钥泄露,攻击者可以伪造有效的JWT,从而获取未授权的访问权限。
    • 应对策略:妥善保管密钥,避免硬编码在代码中。在生产环境中,建议使用环境变量来存储密钥。
    • 代码示例(Node.js with Express)
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
// 从环境变量中获取密钥
const secretKey = process.env.JWT_SECRET_KEY;
app.get('/protected', (req, res) => {
    const token = req.headers['authorization'];
    if (!token) {
        return res.status(401).send('Token is missing');
    }
    try {
        const decoded = jwt.verify(token, secretKey);
        res.send('Access granted:'+ decoded);
    } catch (err) {
        res.status(403).send('Invalid token');
    }
});
  1. 过期时间设置不当风险
    • 风险:如果JWT的过期时间设置过长,攻击者获取到JWT后有较长时间可以利用它进行未授权访问;如果设置过短,可能会给用户带来频繁重新认证的不便。
    • 应对策略:根据应用场景合理设置过期时间。对于长期用户会话,可以结合刷新令牌(refresh token)机制。
    • 代码示例(Python with Flask)
from flask import Flask, request, jsonify
import jwt
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
@app.route('/protected', methods=['GET'])
def protected():
    token = None
    if 'x-access-token' in request.headers:
        token = request.headers['x-access-token']
    if not token:
        return jsonify({'message': 'Token is missing!'}), 401
    try:
        data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
        # 检查过期时间
        if 'exp' in data and data['exp'] < datetime.datetime.utcnow().timestamp():
            return jsonify({'message': 'Token has expired'}), 401
        return jsonify({'message': 'Access granted!'}), 200
    except jwt.ExpiredSignatureError:
        return jsonify({'message': 'Token has expired'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'message': 'Invalid token'}), 401
  1. 重放攻击风险
    • 风险:攻击者截获一个有效的JWT,并多次使用它来访问受保护资源。
    • 应对策略:可以使用一次性使用的JWT,或者结合黑名单机制。在黑名单机制中,当用户登出或者JWT被撤销时,将其加入黑名单。
    • 代码示例(Java with Spring Boot)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.security.Key;
import java.util.ArrayList;
import java.util.List;
@RestController
public class ProtectedResourceController {
    private static final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    private static final List<String> blacklist = new ArrayList<>();
    @GetMapping("/protected")
    public ResponseEntity<String> protectedResource(@RequestHeader("Authorization") String token) {
        if (token == null ||!token.startsWith("Bearer ")) {
            return new ResponseEntity<>("Token is missing", HttpStatus.UNAUTHORIZED);
        }
        String jwtToken = token.substring(7);
        if (blacklist.contains(jwtToken)) {
            return new ResponseEntity<>("Invalid token", HttpStatus.FORBIDDEN);
        }
        try {
            Claims claims = Jwts.parserBuilder()
                  .setSigningKey(key)
                  .build()
                  .parseClaimsJws(jwtToken)
                  .getBody();
            return new ResponseEntity<>("Access granted", HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>("Invalid token", HttpStatus.FORBIDDEN);
        }
    }
}
  1. 算法篡改风险
    • 风险:攻击者可能篡改JWT头部的签名算法,将其改为无签名算法(如none),从而绕过签名验证。
    • 应对策略:在验证JWT时,明确指定预期的签名算法,不使用JWT头部指定的算法,除非进行严格的验证。
    • 代码示例(Ruby with Sinatra)
require'sinatra'
require 'jwt'
set :jwt_secret, 'your_secret_key'
get '/protected' do
    token = request.env['HTTP_AUTHORIZATION']&.gsub('Bearer ', '')
    if token.nil?
        halt 401, 'Token is missing'
    end
    begin
        decoded = JWT.decode(token, settings.jwt_secret, true, { algorithm: 'HS256' })
        'Access granted'
    rescue JWT::VerificationError
        halt 403, 'Invalid token'
    end
end