Llm Data Leakage in Flask with Basic Auth
Llm Data Leakage in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
LLM data leakage in a Flask application using HTTP Basic Authentication can occur when endpoints that return or process language model responses are exposed without proper authorization checks. Basic Auth transmits credentials with each request as a base64-encoded string; if these requests reach an LLM endpoint or route that echoes back model-generated content, sensitive prompts, system instructions, or private data may be included in the response.
Consider a Flask route that proxies user input to an LLM and returns the raw model output:
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/chat', methods=['POST'])
def chat():
user_prompt = request.json.get('prompt', '')
# A vulnerable pattern: no auth check before calling the LLM
llm_resp = requests.post(
'https://api.example.com/v1/chat/completions',
json={'model': 'gpt-3.5-turbo', 'messages': [{'role': 'user', 'content': user_prompt}]},
headers={'Authorization': f'Bearer {os.getenv("LLM_API_KEY")}'}
)
return jsonify(llm_resp.json())
If this route is accessible to unauthenticated users or if Basic Auth credentials are inadvertently omitted in client calls, the response may contain sensitive information such as system prompts, private instructions, or even PII extracted by the model. MiddleBrick’s LLM/AI Security checks specifically detect such unauthenticated LLM endpoints and scan for system prompt leakage patterns, flagging routes that return model output without proper access controls.
In a Flask app using Basic Auth, leakage can also arise from error messages or logs that include model outputs. For example, a debugging route might print or return the full LLM response to the client:
@app.route('/debug', methods=['GET'])
def debug():
# Dangerous: exposes internal LLM behavior and data
return str(llm_resp.json())
Without explicit authorization on the debug route, an attacker could invoke it and harvest leaked prompts or outputs. The scanner’s active prompt injection probes and output scanning for PII, API keys, and executable code help identify these weaknesses when combined with Basic Auth misconfigurations.
Flask applications should ensure that routes interacting with LLMs are protected by robust authorization, validate and sanitize all outputs, and avoid echoing raw model responses. Applying middleware that enforces authentication before allowing access to LLM endpoints reduces the risk of unintended data exposure in both development and production environments.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To remediate data leakage in Flask with Basic Auth, enforce authentication on sensitive routes and avoid exposing raw LLM responses. Use a lightweight verification mechanism that checks credentials on each request before allowing access to LLM endpoints.
Here is a secure pattern using HTTP Basic Auth with explicit checks:
from flask import Flask, request, jsonify, Response
import os
import base64
app = Flask(__name__)
VALID_USER = os.getenv('API_USER', 'admin')
VALID_PASS = os.getenv('API_PASS', 'secret')
def check_auth(username, password):
return username == VALID_USER and password == VALID_PASS
def authenticate():
return Response(
'Could not verify your access level.',
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
def requires_auth(f):
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
decorated.__name__ = f.__name__
return decorated
@app.route('/chat', methods=['POST'])
@requires_auth
def chat():
user_prompt = request.json.get('prompt', '')
llm_resp = requests.post(
'https://api.example.com/v1/chat/completions',
json={'model': 'gpt-3.5-turbo', 'messages': [{'role': 'user', 'content': user_prompt}]},
headers={'Authorization': f'Bearer {os.getenv("LLM_API_KEY")}'}
)
# Avoid returning raw LLM responses that may contain instructions or PII
safe_response = {'answer': llm_resp.json().get('choices', [{}])[0].get('message', {}).get('content', '')}
return jsonify(safe_response)
This pattern ensures that every request to /chat includes valid Basic Auth credentials. The decorator rejects unauthenticated requests with a 401 status and a WWW-Authenticate header, prompting the client to provide credentials.
Additionally, limit the data returned from the LLM by extracting only the necessary fields and filtering out any internal instructions or sensitive context. Avoid debug routes that echo raw model output, and ensure that any logging excludes private prompts or responses. MiddleBrick’s scans can verify that such controls are in place by testing the unauthenticated attack surface and flagging endpoints that return model data without authorization.
For production deployments, combine Basic Auth with HTTPS to protect credentials in transit and rotate secrets regularly. The combination of transport security and strict route-level authorization minimizes the chance of LLM data leakage in Flask services.
Related CWEs: llmSecurity
| CWE ID | Name | Severity |
|---|---|---|
| CWE-754 | Improper Check for Unusual or Exceptional Conditions | MEDIUM |