# JWT Algorithm Confusion via Unverified `alg` Header

Language: Python
Severity: Critical
CWE: CWE-347

## Source
5

## Flow
5-9

## Sink
9

## 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()
        device_payload = jwt.decode(device_token, verification_key, algorithms=['RS256', 'HS256', 'none'])
        device_id = device_payload.get('device_id')
        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': device_payload.get('perms')}), 200
    except jwt.InvalidTokenError:
        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
```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()
        device_payload = jwt.decode(device_token, verification_key, algorithms=['RS256'])
        device_id = device_payload.get('device_id')
        if not device_id:
            return jsonify({'error': 'Invalid device credentials'}), 403
        return jsonify({'status': 'authorized', 'device_id': device_id, 'permissions': device_payload.get('perms')}), 200
    except jwt.InvalidTokenError:
        return jsonify({'error': 'Invalid device credentials'}), 403
```
