Dns Rebinding in Flask with Api Keys
Dns Rebinding in Flask with Api Keys — how this specific combination creates or exposes the vulnerability
DNS rebinding is a client-side network attack where an adversary tricks a victim’s browser into resolving a domain name to an arbitrary IP address, often bypassing same-origin policy protections. In a Flask application that relies on API keys for access control, combining weak host-header validation with predictable key handling can amplify the impact of a rebinding scenario.
Consider a Flask route that enforces access via an API key passed in a request header:
from flask import Flask, request, jsonify
app = Flask(__name__)
VALID_KEY = "s3cr3t-k3y"
@app.route("/admin")
def admin():
if request.headers.get("X-API-Key") != VALID_KEY:
return jsonify({"error": "forbidden"}), 403
return jsonify({"admin": True})
If an attacker registers a domain (e.g., evil.com) that initially resolves to a benign IP and later rebinds to a target internal service (e.g., 127.0.0.1), a victim’s browser can be made to send requests to the Flask app with the attacker-controlled page context. Because the Flask route above only checks the API key header without validating the request origin or host, the rebinding can bypass source-based restrictions if the API key is leaked or reused across zones.
In a black-box scan, middleBrick’s LLM/AI Security checks include system prompt leakage detection and active prompt injection testing; its API-specific checks include BOLA/IDOR, Property Authorization, and Unsafe Consumption tests that can surface routes where authentication is header-only and lacks host or referer validation. Without additional controls, an API key that is valid across different hostnames or ports can be abused via a rebinding vector to reach admin endpoints or internal services that the client does not directly interact with.
Key risk patterns to watch for:
- Accepting API keys in headers without verifying the request’s Host header or origin.
- Using the same API key for both public and administrative endpoints, increasing the blast radius if the key is exfiltrated via rebinding.
- Relying solely on network-based perimeter controls in environments where DNS rebinding can circumvent IP-based assumptions.
Api Keys-Specific Remediation in Flask — concrete code fixes
To reduce DNS rebinding risk when using API keys in Flask, enforce strict host validation, scope keys to specific origins, and avoid key reuse across trust boundaries. Below are concrete, secure patterns you can apply.
1. Validate Host and Origin
Explicitly check the Host header and, when relevant, the Origin header before honoring the API key:
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
VALID_KEY = "s3cr3t-k3y"
ALLOWED_HOSTS = {"api.yourdomain.com", "app.yourdomain.com"}
@app.before_request
def enforce_host_and_key():
if request.path.startswith("/admin"):
if request.headers.get("Host") not in ALLOWED_HOSTS:
abort(403, description="Host not allowed")
if request.headers.get("X-API-Key") != VALID_KEY:
abort(403, description="Invalid API key")
# Optional: enforce Origin for cross-origin requests
origin = request.headers.get("Origin")
if origin and not origin.startswith("https://api.yourdomain.com"):
abort(403, description="Origin not allowed")
This ensures that even if DNS rebinding alters the resolved IP, the Host check will fail for unexpected domains, and the API key is not honored on unauthorized hosts.
2. Key Scoping and Short-Lived Tokens
Instead of a single static key, scope keys per client or per route and rotate them frequently. For example, issue short-lived tokens stored server-side and validated against a small scope:
import time
from flask import Flask, request, jsonify, abort
app = Flask(__name__)
# Simulated store; in production use a secure cache or DB
ACTIVE_TOKENS = {
"token-a": {"expires_at": time.time() + 300, "allowed_host": "api.yourdomain.com"},
}
def validate_token(token, host):
entry = ACTIVE_TOKENS.get(token)
if not entry:
return False
if time.time() > entry["expires_at"]:
return False
if host != entry["allowed_host"]:
return False
return True
@app.route("/data")
def get_data():
token = request.headers.get("X-API-Key")
if not validate_token(token, request.headers.get("Host")):
abort(403, description="Invalid or scoped token")
return jsonify({"data": "safe"})
This approach limits the usefulness of a leaked key and reduces the window for rebinding attacks that rely on a long-lived, broadly trusted key.
3. Use HTTPS and HSTS
Serve your Flask app over HTTPS and include HTTP Strict Transport Security headers to prevent downgrade attacks that could make rebinding easier to execute in mixed-content scenarios:
from flask import Flask
from flask_talisman import Talisman
app = Flask(__name__)
Talisman(app, force_https=True, hsts=True)
Although not a direct API key fix, HTTPS and HSTS reduce the feasibility of DNS rebinding by ensuring responses are delivered over a cryptographically verified channel.
4. Rate Limiting and Monitoring
Apply per-host or per-key rate limits to detect abnormal request patterns that may indicate rebinding attempts:
from flask import Flask
from flask_limiter import Limiter
app = Flask(__name__)
limiter = Limiter(app, key_func=lambda: request.headers.get("Host", "unknown"))
@app.route("/admin")
@limiter.limit("10/minute")
def admin():
if request.headers.get("X-API-Key") != "s3cr3t-k3y":
return jsonify({"error": "forbidden"}), 403
return jsonify({"admin": True})
middleBrick’s Pro plan includes continuous monitoring and can integrate with CI/CD to alert on configurations that may expose keys or weak host validation, helping you detect risky deployments before rebinding scenarios are weaponized.