# JWT Algorithm Confusion Attack via None Algorithm Bypass

Language: Python
Severity: Critical
CWE: CWE-347

## Source
9

## Flow
9-11

## Sink
11

## Vulnerable Code
```python
import jwt
def validate_iot_device_token(raw_token, device_registry):
    unverified_hdr = jwt.get_unverified_header(raw_token)
    algo = unverified_hdr.get('alg', 'HS256')
    dev_secret = device_registry.get('signing_secret')
    try:
        decoded = jwt.decode(raw_token, dev_secret, algorithms=[algo])
        dev_id = decoded.get('device_id')
        role = decoded.get('role')
        if role == 'admin' and dev_id in device_registry.get('trusted_devices', []):
            return {'authorized': True, 'device': dev_id}
        return {'authorized': False}
    except jwt.ExpiredSignatureError:
        return {'authorized': False, 'reason': 'expired'}
    except Exception as e:
        return {'authorized': False, 'reason': str(e)}
```

## Explanation

The algorithm is extracted directly from the unverified JWT header (line 3) and stored in 'algo' (line 4), then passed as the sole allowed algorithm into jwt.decode() (line 7). This allows an attacker to forge a token with 'alg: none', remove the signature entirely, and bypass signature verification — granting unauthorized admin access without knowledge of the signing secret.

## Remediation

The fix removes the extraction of the algorithm from the unverified JWT header and instead uses a server-side constant `ALLOWED_ALGORITHMS = ['HS256']` that is passed to `jwt.decode()`. This prevents an attacker from specifying 'none' or any other unexpected algorithm in the token header, ensuring signature verification always uses the intended cryptographic algorithm. Additionally, a check is added to ensure the signing secret is present before attempting validation.

## Secure Code
```python
import jwt

ALLOWED_ALGORITHMS = ['HS256']

def validate_iot_device_token(raw_token, device_registry):
    dev_secret = device_registry.get('signing_secret')
    if not dev_secret:
        return {'authorized': False, 'reason': 'missing signing secret'}
    try:
        decoded = jwt.decode(raw_token, dev_secret, algorithms=ALLOWED_ALGORITHMS)
        dev_id = decoded.get('device_id')
        role = decoded.get('role')
        if role == 'admin' and dev_id in device_registry.get('trusted_devices', []):
            return {'authorized': True, 'device': dev_id}
        return {'authorized': False}
    except jwt.ExpiredSignatureError:
        return {'authorized': False, 'reason': 'expired'}
    except Exception as e:
        return {'authorized': False, 'reason': str(e)}
```
