{"title":"JWT Algorithm Confusion Attack via Asymmetric-to-Symmetric Key Downgrade","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\ndef validate_iot_device_token():\n    raw_token = request.headers.get('X-Device-Token')\n    with open('/etc/iot/gateway_public.pem', 'r') as pem_file:\n        rsa_pubkey = pem_file.read()\n    try:\n        hdr = jwt.get_unverified_header(raw_token)\n        chosen_algo = hdr.get('alg', 'RS256')\n        device_claims = jwt.decode(raw_token, rsa_pubkey, algorithms=[chosen_algo])\n        return jsonify({'device_id': device_claims['did'], 'authorized': True})\n    except jwt.ExpiredSignatureError:\n        return jsonify({'error': 'token_expired'}), 401\n    except Exception as exc:\n        return jsonify({'error': str(exc)}), 403","explanation":"The server reads the 'alg' field from the unverified JWT header (line 8) and passes it directly as the trusted algorithm to jwt.decode() (line 10). An attacker can craft a token with 'alg': 'HS256' in the header and sign it using the RSA public key (which is publicly known) as the HMAC-SHA256 secret. Since jwt.decode() is instructed to use HS256 and the RSA public key is used as the HMAC secret, the signature verification succeeds, completely bypassing RS256-based authentication.","remediation":"The fix removes the extraction of the 'alg' field from the unverified JWT header and instead hardcodes the allowed algorithm list to ['RS256'] in the jwt.decode() call. This prevents an attacker from downgrading the algorithm to HS256 (or any other symmetric algorithm) and forging a valid token using the publicly known RSA public key as an HMAC secret.","secure_code":"import jwt\nfrom flask import request, jsonify\ndef validate_iot_device_token():\n    raw_token = request.headers.get('X-Device-Token')\n    with open('/etc/iot/gateway_public.pem', 'r') as pem_file:\n        rsa_pubkey = pem_file.read()\n    try:\n        device_claims = jwt.decode(raw_token, rsa_pubkey, algorithms=['RS256'])\n        return jsonify({'device_id': device_claims['did'], 'authorized': True})\n    except jwt.ExpiredSignatureError:\n        return jsonify({'error': 'token_expired'}), 401\n    except Exception as exc:\n        return jsonify({'error': str(exc)}), 403"}