{"title":"Insecure Direct Object Reference via Flask Session Forgery with Weak Secret Key","language":"Python","severity":"Critical","cwe":"CWE-89","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"from flask import Flask, session, jsonify\nimport sqlite3\napp = Flask(__name__)\napp.secret_key = 'iot2024'\n@app.route('/device/telemetry')\ndef fetch_device_data():\n    dev_id = session.get('device_id')\n    owner = session.get('owner_uid')\n    conn = sqlite3.connect('iot_platform.db')\n    cur = conn.cursor()\n    cur.execute(f\"SELECT readings, firmware_ver, last_ping FROM devices WHERE dev_id={dev_id} AND owner={owner}\")\n    rec = cur.fetchone()\n    conn.close()\n    if not rec:\n        return jsonify({'err': 'not found'}), 404\n    return jsonify({'readings': rec[0], 'firmware': rec[1], 'last_ping': rec[2]})","explanation":"The weak secret key 'iot2024' on line 4 allows attackers to forge Flask session cookies using tools like flask-unsign, enabling injection of arbitrary 'device_id' and 'owner_uid' values. These forged session values are retrieved on lines 7-8 and directly interpolated into a raw SQL query via an f-string on line 11, enabling both IDOR (accessing other users' device data) and SQL injection without any sanitization or parameterization.","remediation":"The fix addresses all vulnerabilities in the flow: (1) the weak hardcoded secret key 'iot2024' is replaced with a cryptographically strong key sourced from an environment variable or generated via os.urandom, preventing session cookie forgery; (2) the raw f-string SQL query is replaced with parameterized queries using '?' placeholders, eliminating SQL injection; (3) input validation ensures dev_id and owner are valid integers before use, and missing session values return a 401 error.","secure_code":"import os\nfrom flask import Flask, session, jsonify\nimport sqlite3\n\napp = Flask(__name__)\napp.secret_key = os.environ.get('FLASK_SECRET_KEY') or os.urandom(64).hex()\n\n\n@app.route('/device/telemetry')\ndef fetch_device_data():\n    dev_id = session.get('device_id')\n    owner = session.get('owner_uid')\n\n    if dev_id is None or owner is None:\n        return jsonify({'err': 'unauthorized'}), 401\n\n    # Validate that dev_id and owner are integers to prevent injection\n    try:\n        dev_id = int(dev_id)\n        owner = int(owner)\n    except (ValueError, TypeError):\n        return jsonify({'err': 'invalid session data'}), 400\n\n    conn = sqlite3.connect('iot_platform.db')\n    cur = conn.cursor()\n    cur.execute(\n        \"SELECT readings, firmware_ver, last_ping FROM devices WHERE dev_id=? AND owner=?\",\n        (dev_id, owner)\n    )\n    rec = cur.fetchone()\n    conn.close()\n\n    if not rec:\n        return jsonify({'err': 'not found'}), 404\n    return jsonify({'readings': rec[0], 'firmware': rec[1], 'last_ping': rec[2]})"}