# CRLF Injection in Flask Response Headers

Language: Python
Severity: High
CWE: CWE-113

## Source
7-8

## Flow
7-11, 8-12

## Sink
11-12

## Vulnerable Code
```python
from flask import Flask, request, Response

app = Flask(__name__)

@app.route('/iot/device/telemetry')
def stream_device_telemetry():
    device_id = request.args.get('device_id', 'unknown')
    correlation_id = request.args.get('correlation_id', '')
    telemetry_data = f'{{"device":"{device_id}","status":"active"}}'
    resp = Response(telemetry_data, mimetype='application/json')
    resp.headers['X-Correlation-ID'] = correlation_id
    resp.headers['X-Device-ID'] = device_id
    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
```python
from flask import Flask, request, Response
import re

app = Flask(__name__)

def sanitize_header_value(value):
    """Remove CRLF characters and other control characters to prevent header injection."""
    if value is None:
        return ''
    # Remove carriage return, line feed, and other control characters
    sanitized = re.sub(r'[\r\n\x00-\x1f\x7f]', '', value)
    return sanitized

def sanitize_device_id(device_id):
    """Validate and sanitize device_id to only allow safe characters."""
    if device_id is None:
        return 'unknown'
    # Allow only alphanumeric characters, hyphens, underscores, and dots
    sanitized = re.sub(r'[^a-zA-Z0-9.\-_]', '', device_id)
    return sanitized if sanitized else 'unknown'

@app.route('/iot/device/telemetry')
def stream_device_telemetry():
    device_id = request.args.get('device_id', 'unknown')
    correlation_id = request.args.get('correlation_id', '')

    # Sanitize inputs before using in headers and response body
    safe_device_id = sanitize_device_id(device_id)
    safe_correlation_id = sanitize_header_value(correlation_id)

    telemetry_data = f'{{"device":"{safe_device_id}","status":"active"}}'
    resp = Response(telemetry_data, mimetype='application/json')
    resp.headers['X-Correlation-ID'] = safe_correlation_id
    resp.headers['X-Device-ID'] = safe_device_id
    return resp
```
