# Insecure Randomness via random.randrange() for API Token Generation

Language: Python
Severity: High
CWE: CWE-338

## Source
7

## Flow
7-8-9-10-11

## Sink
8, 10

## Vulnerable Code
```python
import random
import hashlib
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/iot/provision/<device_id>')
def provision_iot_device(device_id):
    nonce = random.randrange(100000, 999999)
    device_secret = hashlib.sha256(f"{device_id}:{nonce}".encode()).hexdigest()
    activation_key = f"IOT-{nonce}-{device_id[:8]}"
    store_device_credentials(device_id, device_secret, activation_key)
    return jsonify({"device_id": device_id, "activation_key": activation_key, "secret": device_secret})
```

## Explanation

The code uses random.randrange() instead of secrets module for generating cryptographic tokens. This produces predictable pseudo-random values that can be brute-forced or predicted by attackers who can observe multiple nonce values. The nonce is exposed in the activation_key and used to derive device_secret, allowing attackers to predict future device credentials.

## Remediation

The fix replaces the insecure random.randrange() with the cryptographically secure secrets module. The nonce is now a 32-byte hex token (256 bits of entropy) making it unpredictable, and the activation key uses a separate secrets.token_urlsafe() value so the nonce used in secret derivation is never exposed in the response.

## Secure Code
```python
import secrets
import hashlib
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/iot/provision/<device_id>')
def provision_iot_device(device_id):
    nonce = secrets.token_hex(32)
    device_secret = hashlib.sha256(f"{device_id}:{nonce}".encode()).hexdigest()
    activation_key = f"IOT-{secrets.token_urlsafe(16)}-{device_id[:8]}"
    store_device_credentials(device_id, device_secret, activation_key)
    return jsonify({"device_id": device_id, "activation_key": activation_key, "secret": device_secret})
```
