{"title":"Insecure Randomness via random.randrange() for API Token Generation","language":"Python","severity":"High","cwe":"CWE-338","source_lines":[7],"flow_lines":[7,8,9,10,11],"sink_lines":[8,10],"vulnerable_code":"import random\nimport hashlib\nfrom flask import Flask, jsonify\napp = Flask(__name__)\n@app.route('/iot/provision/<device_id>')\ndef provision_iot_device(device_id):\n    nonce = random.randrange(100000, 999999)\n    device_secret = hashlib.sha256(f\"{device_id}:{nonce}\".encode()).hexdigest()\n    activation_key = f\"IOT-{nonce}-{device_id[:8]}\"\n    store_device_credentials(device_id, device_secret, activation_key)\n    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":"import secrets\nimport hashlib\nfrom flask import Flask, jsonify\napp = Flask(__name__)\n@app.route('/iot/provision/<device_id>')\ndef provision_iot_device(device_id):\n    nonce = secrets.token_hex(32)\n    device_secret = hashlib.sha256(f\"{device_id}:{nonce}\".encode()).hexdigest()\n    activation_key = f\"IOT-{secrets.token_urlsafe(16)}-{device_id[:8]}\"\n    store_device_credentials(device_id, device_secret, activation_key)\n    return jsonify({\"device_id\": device_id, \"activation_key\": activation_key, \"secret\": device_secret})"}