{"title":"JWT Algorithm Confusion via alg=none Acceptance","language":"Python","severity":"Critical","cwe":"CWE-347","source_lines":[9],"flow_lines":[9,10],"sink_lines":[10],"vulnerable_code":"import jwt\nimport json\nfrom flask import request, jsonify\n\ndef verify_iot_device_token(auth_header):\n    device_token = auth_header.replace('Bearer ', '')\n    token_parts = device_token.split('.')\n    header_data = json.loads(jwt.utils.base64url_decode(token_parts[0]))\n    allowed_algos = ['HS256', 'RS256', header_data.get('alg')]\n    decoded = jwt.decode(device_token, options={'verify_signature': False}, algorithms=allowed_algos)\n    if decoded.get('device_id') and decoded.get('permissions'):\n        return {'status': 'authenticated', 'device': decoded['device_id'], 'access': decoded['permissions']}\n    return {'status': 'denied'}","explanation":"The code extracts the 'alg' header from an unverified JWT token and adds it to the allowed algorithms list, then decodes the token with signature verification disabled. This allows attackers to create tokens with 'alg=none', bypassing all cryptographic validation and forging arbitrary device_id and permissions claims.","remediation":"The fix removes the dynamic algorithm list that trusted the token's own 'alg' header claim and enforces a strict, server-side allowlist of only 'HS256'. Signature verification is now enabled (the default behavior) by removing the 'options={\"verify_signature\": False}' parameter, ensuring all tokens are cryptographically validated against a known secret key before being accepted.","secure_code":"import jwt\nfrom flask import request, jsonify\n\nSECRET_KEY = None  # Must be configured with actual secret key at startup\n\ndef verify_iot_device_token(auth_header):\n    device_token = auth_header.replace('Bearer ', '')\n    decoded = jwt.decode(device_token, SECRET_KEY, algorithms=['HS256'])\n    if decoded.get('device_id') and decoded.get('permissions'):\n        return {'status': 'authenticated', 'device': decoded['device_id'], 'access': decoded['permissions']}\n    return {'status': 'denied'}"}