{"title":"JWT Algorithm Confusion via Unverified `alg` Header","language":"Python","severity":"Critical","cwe":"CWE-347","source_lines":[5],"flow_lines":[5,9],"sink_lines":[9],"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        with open('/etc/iot/device_public.pem', 'r') as key_file:\n            verification_key = key_file.read()\n        device_payload = jwt.decode(device_token, verification_key, algorithms=['RS256', 'HS256', 'none'])\n        device_id = device_payload.get('device_id')\n        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': device_payload.get('perms')}), 200\n    except jwt.InvalidTokenError:\n        return jsonify({'error': 'Invalid device credentials'}), 403","explanation":"The code accepts JWT tokens with multiple algorithms including 'none', allowing attackers to bypass signature verification. An attacker can craft a token with 'alg: none' and no signature, or use HS256 with the public key as the HMAC secret, gaining unauthorized access with arbitrary device_id and elevated permissions.","remediation":"The fix restricts the allowed algorithms to only 'RS256', removing both 'HS256' and 'none' from the accepted list. This prevents algorithm confusion attacks where an attacker could forge tokens using the 'none' algorithm (no signature) or use the public key as an HMAC secret with HS256. Additionally, a validation check for device_id presence was added to ensure the token contains required claims.","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        with open('/etc/iot/device_public.pem', 'r') as key_file:\n            verification_key = key_file.read()\n        device_payload = jwt.decode(device_token, verification_key, algorithms=['RS256'])\n        device_id = device_payload.get('device_id')\n        if not device_id:\n            return jsonify({'error': 'Invalid device credentials'}), 403\n        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': device_payload.get('perms')}), 200\n    except jwt.InvalidTokenError:\n        return jsonify({'error': 'Invalid device credentials'}), 403"}