{"title":"JWT Algorithm Confusion via Public-Key-as-HMAC-Secret Misuse","language":"Python","severity":"Critical","cwe":"CWE-347","source_lines":[3],"flow_lines":[3,7],"sink_lines":[7],"vulnerable_code":"import jwt\nfrom flask import request, jsonify\n\ndef verify_iot_device_token(device_jwt):\n    with open('/etc/iot/device_public.pem', 'r') as key_file:\n        rsa_public_key = key_file.read()\n    try:\n        device_data = jwt.decode(device_jwt, rsa_public_key, algorithms=['RS256', 'HS256'])\n        device_id = device_data.get('device_id')\n        if device_id:\n            return {'status': 'authorized', 'device_id': device_id, 'permissions': device_data.get('permissions', [])}\n    except jwt.InvalidTokenError:\n        return {'status': 'unauthorized'}\n    return {'status': 'error'}","explanation":"The JWT verification allows both RS256 (asymmetric) and HS256 (symmetric) algorithms. An attacker can create a malicious JWT signed with HS256 using the public RSA key as the HMAC secret. Since the public key is known, attackers can forge valid tokens with arbitrary claims including elevated permissions.","remediation":"The fix restricts the allowed algorithms list to only 'RS256', removing 'HS256' from the accepted algorithms. This prevents the algorithm confusion attack where an attacker could sign a forged token using HS256 with the publicly available RSA public key as the HMAC secret, since the decoder will now reject any token that isn't signed with RS256.","secure_code":"import jwt\nfrom flask import request, jsonify\n\ndef verify_iot_device_token(device_jwt):\n    with open('/etc/iot/device_public.pem', 'r') as key_file:\n        rsa_public_key = key_file.read()\n    try:\n        device_data = jwt.decode(device_jwt, rsa_public_key, algorithms=['RS256'])\n        device_id = device_data.get('device_id')\n        if device_id:\n            return {'status': 'authorized', 'device_id': device_id, 'permissions': device_data.get('permissions', [])}\n    except jwt.InvalidTokenError:\n        return {'status': 'unauthorized'}\n    return {'status': 'error'}"}