{"title":"Insecure Randomness via random.choice() for Security Tokens","language":"Python","severity":"High","cwe":"CWE-338","source_lines":[10],"flow_lines":[10],"sink_lines":[10],"vulnerable_code":"import random\nimport string\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\niot_device_sessions = {}\n\n@app.route('/api/iot/provision', methods=['POST'])\ndef provision_device():\n    device_id = request.json.get('device_id')\n    auth_token = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))\n    iot_device_sessions[device_id] = {'token': auth_token, 'provisioned': True}\n    return jsonify({'device_id': device_id, 'auth_token': auth_token, 'status': 'provisioned'})\n\n@app.route('/api/iot/telemetry', methods=['POST'])\ndef receive_telemetry():\n    token = request.headers.get('X-Device-Token')\n    if token in [v['token'] for v in iot_device_sessions.values()]:\n        return jsonify({'status': 'telemetry_accepted'})\n    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":"import secrets\nimport string\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\niot_device_sessions = {}\n\n@app.route('/api/iot/provision', methods=['POST'])\ndef provision_device():\n    device_id = request.json.get('device_id')\n    auth_token = secrets.token_urlsafe(32)\n    iot_device_sessions[device_id] = {'token': auth_token, 'provisioned': True}\n    return jsonify({'device_id': device_id, 'auth_token': auth_token, 'status': 'provisioned'})\n\n@app.route('/api/iot/telemetry', methods=['POST'])\ndef receive_telemetry():\n    token = request.headers.get('X-Device-Token')\n    if token and any(secrets.compare_digest(token, v['token']) for v in iot_device_sessions.values()):\n        return jsonify({'status': 'telemetry_accepted'})\n    return jsonify({'error': 'unauthorized'}), 401"}