# Insecure Randomness via random.choice() for Security Tokens

Language: Python
Severity: High
CWE: CWE-338

## Source
10

## Flow
10

## Sink
10

## Vulnerable Code
```python
import random
import string
from flask import Flask, request, jsonify

app = Flask(__name__)
iot_device_sessions = {}

@app.route('/api/iot/provision', methods=['POST'])
def provision_device():
    device_id = request.json.get('device_id')
    auth_token = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))
    iot_device_sessions[device_id] = {'token': auth_token, 'provisioned': True}
    return jsonify({'device_id': device_id, 'auth_token': auth_token, 'status': 'provisioned'})

@app.route('/api/iot/telemetry', methods=['POST'])
def receive_telemetry():
    token = request.headers.get('X-Device-Token')
    if token in [v['token'] for v in iot_device_sessions.values()]:
        return jsonify({'status': 'telemetry_accepted'})
    return jsonify({'error': 'unauthorized'}), 401
```

## Explanation

The code uses Python's random.choice() from the random module to generate authentication tokens for IoT devices. The random module uses a pseudo-random number generator (Mersenne Twister) that is predictable and not cryptographically secure, making tokens guessable by attackers who can observe patterns or seed the PRNG state.

## Remediation

The fix replaces the insecure `random.choice()` with `secrets.token_urlsafe(32)`, which uses a cryptographically secure random number generator suitable for generating authentication tokens. Additionally, the token comparison in the telemetry endpoint now uses `secrets.compare_digest()` to prevent timing attacks during token validation.

## Secure Code
```python
import secrets
import string
from flask import Flask, request, jsonify

app = Flask(__name__)
iot_device_sessions = {}

@app.route('/api/iot/provision', methods=['POST'])
def provision_device():
    device_id = request.json.get('device_id')
    auth_token = secrets.token_urlsafe(32)
    iot_device_sessions[device_id] = {'token': auth_token, 'provisioned': True}
    return jsonify({'device_id': device_id, 'auth_token': auth_token, 'status': 'provisioned'})

@app.route('/api/iot/telemetry', methods=['POST'])
def receive_telemetry():
    token = request.headers.get('X-Device-Token')
    if token and any(secrets.compare_digest(token, v['token']) for v in iot_device_sessions.values()):
        return jsonify({'status': 'telemetry_accepted'})
    return jsonify({'error': 'unauthorized'}), 401
```
