{"title":"JWT Algorithm Confusion via PyJWT `alg` Header Trust","language":"Python","severity":"Critical","cwe":"CWE-327","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"import jwt\nfrom flask import request, jsonify\n\ndef validate_iot_device_token():\n    device_token = request.headers.get('X-Device-Auth')\n    if not device_token:\n        return jsonify({'error': 'Missing device token'}), 401\n    try:\n        public_key = open('/etc/iot/device_rsa.pub').read()\n        device_claims = jwt.decode(device_token, public_key, algorithms=['RS256', 'HS256'])\n        device_id = device_claims.get('device_id')\n        if device_id:\n            return jsonify({'status': 'authorized', 'device': device_id, 'admin': device_claims.get('is_admin', False)})\n    except jwt.InvalidTokenError:\n        return jsonify({'error': 'Invalid token'}), 403\n    return jsonify({'error': 'Authorization failed'}), 403","explanation":"The code allows both RS256 (asymmetric) and HS256 (symmetric) algorithms in jwt.decode(). An attacker can craft a token with 'alg': 'HS256' in the header and sign it using the public key as the HMAC secret, bypassing RSA signature verification and gaining unauthorized access with elevated privileges.","remediation":"The fix removes 'HS256' from the allowed algorithms list, restricting token verification to only 'RS256' (asymmetric). This prevents algorithm confusion attacks where an attacker could sign a forged token using the public key as an HMAC secret with HS256, since the decoder will now reject any token that doesn't use RS256.","secure_code":"import jwt\nfrom flask import request, jsonify\n\ndef validate_iot_device_token():\n    device_token = request.headers.get('X-Device-Auth')\n    if not device_token:\n        return jsonify({'error': 'Missing device token'}), 401\n    try:\n        public_key = open('/etc/iot/device_rsa.pub').read()\n        device_claims = jwt.decode(device_token, public_key, algorithms=['RS256'])\n        device_id = device_claims.get('device_id')\n        if device_id:\n            return jsonify({'status': 'authorized', 'device': device_id, 'admin': device_claims.get('is_admin', False)})\n    except jwt.InvalidTokenError:\n        return jsonify({'error': 'Invalid token'}), 403\n    return jsonify({'error': 'Authorization failed'}), 403"}