# Insecure Direct Object Reference via Flask Session Forgery with Weak Secret Key

Language: Python
Severity: Critical
CWE: CWE-89

## Source
9

## Flow
9-11

## Sink
11

## Vulnerable Code
```python
from flask import Flask, session, jsonify
import sqlite3
app = Flask(__name__)
app.secret_key = 'iot2024'
@app.route('/device/telemetry')
def fetch_device_data():
    dev_id = session.get('device_id')
    owner = session.get('owner_uid')
    conn = sqlite3.connect('iot_platform.db')
    cur = conn.cursor()
    cur.execute(f"SELECT readings, firmware_ver, last_ping FROM devices WHERE dev_id={dev_id} AND owner={owner}")
    rec = cur.fetchone()
    conn.close()
    if not rec:
        return jsonify({'err': 'not found'}), 404
    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
```python
import os
from flask import Flask, session, jsonify
import sqlite3

app = Flask(__name__)
app.secret_key = os.environ.get('FLASK_SECRET_KEY') or os.urandom(64).hex()


@app.route('/device/telemetry')
def fetch_device_data():
    dev_id = session.get('device_id')
    owner = session.get('owner_uid')

    if dev_id is None or owner is None:
        return jsonify({'err': 'unauthorized'}), 401

    # Validate that dev_id and owner are integers to prevent injection
    try:
        dev_id = int(dev_id)
        owner = int(owner)
    except (ValueError, TypeError):
        return jsonify({'err': 'invalid session data'}), 400

    conn = sqlite3.connect('iot_platform.db')
    cur = conn.cursor()
    cur.execute(
        "SELECT readings, firmware_ver, last_ping FROM devices WHERE dev_id=? AND owner=?",
        (dev_id, owner)
    )
    rec = cur.fetchone()
    conn.close()

    if not rec:
        return jsonify({'err': 'not found'}), 404
    return jsonify({'readings': rec[0], 'firmware': rec[1], 'last_ping': rec[2]})
```
