{"title":"CRLF Injection in Flask Response Headers","language":"Python","severity":"High","cwe":"CWE-113","source_lines":[7,8],"flow_lines":[7,11,8,12],"sink_lines":[11,12],"vulnerable_code":"from flask import Flask, request, Response\n\napp = Flask(__name__)\n\n@app.route('/iot/device/telemetry')\ndef stream_device_telemetry():\n    device_id = request.args.get('device_id', 'unknown')\n    correlation_id = request.args.get('correlation_id', '')\n    telemetry_data = f'{{\"device\":\"{device_id}\",\"status\":\"active\"}}'\n    resp = Response(telemetry_data, mimetype='application/json')\n    resp.headers['X-Correlation-ID'] = correlation_id\n    resp.headers['X-Device-ID'] = device_id\n    return resp","explanation":"The application accepts user-controlled input from 'correlation_id' and 'device_id' query parameters and directly injects them into HTTP response headers without sanitization. An attacker can inject CRLF characters (\\r\\n) to add arbitrary headers or inject content into the response body, potentially enabling HTTP response splitting attacks, session fixation, or XSS.","remediation":"The fix introduces two sanitization functions: one that strips all CRLF and control characters from header values, and another that restricts device_id to a whitelist of safe characters (alphanumeric, hyphens, underscores, dots). Both user-supplied parameters are sanitized before being used in response headers or the response body, preventing CRLF injection and HTTP response splitting attacks.","secure_code":"from flask import Flask, request, Response\nimport re\n\napp = Flask(__name__)\n\ndef sanitize_header_value(value):\n    \"\"\"Remove CRLF characters and other control characters to prevent header injection.\"\"\"\n    if value is None:\n        return ''\n    # Remove carriage return, line feed, and other control characters\n    sanitized = re.sub(r'[\\r\\n\\x00-\\x1f\\x7f]', '', value)\n    return sanitized\n\ndef sanitize_device_id(device_id):\n    \"\"\"Validate and sanitize device_id to only allow safe characters.\"\"\"\n    if device_id is None:\n        return 'unknown'\n    # Allow only alphanumeric characters, hyphens, underscores, and dots\n    sanitized = re.sub(r'[^a-zA-Z0-9.\\-_]', '', device_id)\n    return sanitized if sanitized else 'unknown'\n\n@app.route('/iot/device/telemetry')\ndef stream_device_telemetry():\n    device_id = request.args.get('device_id', 'unknown')\n    correlation_id = request.args.get('correlation_id', '')\n\n    # Sanitize inputs before using in headers and response body\n    safe_device_id = sanitize_device_id(device_id)\n    safe_correlation_id = sanitize_header_value(correlation_id)\n\n    telemetry_data = f'{{\"device\":\"{safe_device_id}\",\"status\":\"active\"}}'\n    resp = Response(telemetry_data, mimetype='application/json')\n    resp.headers['X-Correlation-ID'] = safe_correlation_id\n    resp.headers['X-Device-ID'] = safe_device_id\n    return resp"}