Prompt Injection in Flask with Basic Auth
Prompt Injection in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
Prompt injection in a Flask API that uses HTTP Basic Authentication arises when user-controlled input can reach the system prompt sent to an LLM endpoint. In this combination, the client first sends an Authorization header containing a base64-encoded username:password pair. If the application embeds user-supplied data—such as query parameters, JSON bodies, or headers—into the prompt before forwarding it to an LLM, an attacker can craft a request that alters the intended behavior of the model.
Consider a Flask route that builds a prompt from user input and then calls an unauthenticated LLM endpoint. Because Basic Authentication applies only to the Flask route and not to the LLM call, the user can attempt to inject instructions or extract the system prompt. For example, an attacker might provide a payload that closes the original instruction block and appends a new directive. If the application does not validate or escape the input, the LLM may comply with the injected instruction, leading to system prompt leakage, unauthorized actions, or data exfiltration.
The risk is compounded when the LLM endpoint itself is unauthenticated. Without controls on the LLM side, an attacker who influences the prompt can potentially cause the model to reveal its system instructions, bypass intended guardrails, or misuse the tool-calling configuration. This scenario is especially relevant when the Flask service acts as a proxy that concatenates static prompt text with dynamic user data and forwards the result to the LLM. Because the LLM security checks include system prompt leakage detection and active prompt injection testing with probes such as system prompt extraction and instruction override, such vulnerabilities are detectable through these specific analyses.
In a real-world-like test, a payload designed to escape the intended role can appear as extra newline characters and explicit override commands. For instance, if the user input is appended directly into the prompt template, an attacker might supply a string that terminates the original instructions and begins a new block that requests the model to disclose its initial instructions or simulate tool usage. Because the LLM security module runs sequential probes—including system prompt extraction and DAN jailbreak attempts—an API that fails to sanitize inputs will often reveal the system prompt or exhibit unintended behavior during these tests.
Additionally, when Basic Authentication credentials are handled incorrectly—such as being logged or echoed in error messages—they can further expose sensitive context. Even though authentication protects the Flask route, the integrity of the prompt depends on how user data is incorporated. Flasks that deserialize JSON bodies using request.get_json() and then directly interpolate fields like username or query into the prompt are at risk if those fields reach the LLM. Proper input validation and strict separation between authentication context and prompt content are essential to reduce the attack surface.
Basic Auth-Specific Remediation in Flask — concrete code fixes
To mitigate prompt injection in Flask with Basic Authentication, ensure that user-controlled data never directly modifies the system prompt or instruction block. Use strict input validation, keep the authentication layer separate from LLM prompt construction, and apply output scanning for sensitive content. The following examples illustrate a secure approach.
Secure Prompt Construction with Basic Auth
First, validate and sanitize any user input before it touches the prompt. Use allowlists for expected values and avoid concatenating raw user strings into system instructions.
import base64
from flask import Flask, request, jsonify
app = Flask(__name__)
def verify_basic_auth():
auth_header = request.headers.get('Authorization')
if not auth_header or not auth_header.startswith('Basic '):
return False
try:
encoded = auth_header.split(' ')[1]
decoded = base64.b64decode(encoded).decode('utf-8')
username, password = decoded.split(':', 1)
# Replace with secure credential verification
if username == 'admin' and password == 'S3cur3P@ss!':
return True
except Exception:
return False
return False
@app.route('/query', methods=['POST'])
def query_api():
if not verify_basic_auth():
return jsonify({'error': 'Unauthorized'}), 401
data = request.get_json(silent=True) or {}
user_query = data.get('query', '')
# Validate and sanitize input
if not isinstance(user_query, str) or len(user_query) > 200:
return jsonify({'error': 'Invalid query'}), 400
# Build prompt without injecting raw user input into system instructions
system_prompt = 'You are a helpful assistant that answers questions about our public API.'
user_prompt = f'User query: {user_query}'
# Here you would call the LLM endpoint with system_prompt and user_prompt separately
# Example pseudo-call:
# llm_response = call_llm(system=system_prompt, user=user_prompt)
llm_response = {'content': 'This is a simulated response.'}
# Apply output scanning for PII, API keys, and code (not implemented here)
return jsonify({'response': llm_response['content']})
Remediation Checklist
- Never embed raw user input into the system prompt.
- Use Basic Authentication only for route-level access control; do not rely on it to protect LLM interactions.
- Validate input types, lengths, and character sets before using them in prompts.
- Separate the authentication context from the prompt-building logic to reduce injection surface.
- Scan LLM outputs for PII, API keys, and executable code before returning responses to clients.
By applying these practices, you keep authentication concerns distinct from LLM prompt handling, reducing the likelihood that an attacker can manipulate the model through carefully crafted inputs. The combination of input validation, strict prompt design, and output screening aligns with the checks performed by tools that test for system prompt leakage and active prompt injection.
Related CWEs: llmSecurity
| CWE ID | Name | Severity |
|---|---|---|
| CWE-754 | Improper Check for Unusual or Exceptional Conditions | MEDIUM |