# JWT Algorithm Confusion via Unverified `alg` Header

Language: Python
Severity: Critical
CWE: CWE-347

## Source
5

## Flow
5-10

## Sink
10

## Vulnerable Code
```python
import jwt
from flask import request, jsonify

def validate_iot_device_token():
    device_token = request.headers.get('X-Device-Auth')
    if not device_token:
        return jsonify({'error': 'Missing device token'}), 401
    try:
        with open('/etc/iot/device_public.pem', 'r') as key_file:
            verification_key = key_file.read()
        payload = jwt.decode(device_token, verification_key, algorithms=['RS256', 'HS256', 'none'])
        device_id = payload.get('device_id')
        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': payload.get('perms')}), 200
    except jwt.InvalidTokenError:
        return jsonify({'error': 'Invalid device credentials'}), 403
```

## Explanation

The code accepts an untrusted JWT token from the X-Device-Auth header and decodes it using jwt.decode() with multiple algorithms including 'HS256' and 'none'. This allows an attacker to exploit algorithm confusion by changing the 'alg' header to 'HS256' and signing the token with the public key (which is treated as an HMAC secret), or by setting 'alg' to 'none' to bypass signature verification entirely.

## Remediation

The fix restricts the allowed algorithms to only 'RS256', which is the expected asymmetric algorithm for verifying tokens signed with the corresponding private key. This prevents algorithm confusion attacks where an attacker could switch to 'HS256' (using the public key as an HMAC secret) or 'none' (bypassing signature verification entirely).

## Secure Code
```python
import jwt
from flask import request, jsonify

def validate_iot_device_token():
    device_token = request.headers.get('X-Device-Auth')
    if not device_token:
        return jsonify({'error': 'Missing device token'}), 401
    try:
        with open('/etc/iot/device_public.pem', 'r') as key_file:
            verification_key = key_file.read()
        payload = jwt.decode(device_token, verification_key, algorithms=['RS256'])
        device_id = payload.get('device_id')
        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': payload.get('perms')}), 200
    except jwt.InvalidTokenError:
        return jsonify({'error': 'Invalid device credentials'}), 403
```
