Dns Rebinding in Flask (Python)
Dns Rebinding in Flask with Python — how this specific combination creates or exposes the vulnerability
DNS rebinding is a client-side attack where a malicious webpage changes the DNS record of a hostname to point to an internal IP address after the browser has loaded the page. When a Flask app is hosted in Python without explicit host binding and relies on default or permissive settings, it can inadvertently expose internal services to the rebinding victim. In Flask, the development server binds to 0.0.0.0 by default when app.run() is used without specifying host. This allows the server to accept connections from any interface, including those that resolve to internal addresses after a DNS rebind.
Consider a Flask app running locally or in a staging environment with app.run() (no explicit host/port constraints). An attacker crafts a page that initially loads assets from the Flask host, then uses JavaScript to rebind that hostname to 127.0.0.1 or another internal IP. Because Flask listens on all interfaces, the browser’s subsequent requests are accepted by the local Flask process. If the Flask app exposes endpoints that return sensitive data or perform actions on behalf of the authenticated user (e.g., internal health checks or administrative routes), the rebinding can lead to unauthorized internal access. This becomes especially relevant when combined with other weaknesses such as missing authorization on internal endpoints or overly permissive CORS, enabling the attacker to chain behaviors to reach internal systems that would otherwise be protected by network boundaries.
The risk is amplified in Python environments where developers may run Flask with debug=True or without proper request validation. Debug modes can expose interactive debugger consoles that are bound to the same host and may respond to rebinded requests if not properly isolated. Additionally, if the Flask app integrates with internal services via HTTP clients (e.g., using requests or urllib) based on parameters provided by the user, an attacker can leverage rebinding to direct those internal calls to sensitive internal endpoints, bypassing firewall rules that assume server-side code will only call trusted hosts.
In the context of middleBrick’s 12 security checks, DNS rebinding surfaces under Input Validation and Property Authorization/SSRF-related behaviors. The scanner tests whether the API surface reflects internal resolution paths or permits resolution to private IPs after a change in DNS. For Flask apps, the findings emphasize the need to explicitly bind to specific interfaces, validate and sanitize host inputs, and avoid exposing internal endpoints to unauthenticated or cross-origin contexts.
Python-Specific Remediation in Flask — concrete code fixes
Remediation centers on explicitly controlling which network interfaces Flask listens on and ensuring that internal endpoints are never exposed to unauthenticated or cross-origin traffic. Below are concrete code examples for secure Flask configurations in Python.
Bind explicitly to localhost or a specific interface
Avoid relying on Flask’s defaults. In production, bind to 127.0.0.1 if the app should only accept local connections, or to a specific public/private IP if remote access is required.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, secure world"
if __name__ == "__main__":
# Bind explicitly to localhost for development
app.run(host="127.0.0.1", port=5000, debug=False)
When deploying with a production WSGI server (e.g., Gunicorn), the host binding is controlled by the server command, not by Flask’s run, but the principle remains: do not expose administrative or debug endpoints to public interfaces.
Disable debug mode and restrict host exposure
Ensure debug=False in production and avoid using app.run(debug=True) in any publicly reachable environment. Debug mode can enable the interactive debugger which may respond to rebinded requests if not properly isolated.
if __name__ == "__main__":
# Never enable debug on public interfaces
app.run(host="0.0.0.0", port=8000, debug=False)
Validate and restrict origins for CORS and internal clients
If the Flask app serves a web frontend or provides HTTP client functionality that accepts target URLs, validate origins and reject private IP ranges after rebinding. For example, when using CORS, restrict origins explicitly and avoid wildcard rules in production.
from flask import Flask, request, abort
from werkzeug.http import is_ip_in_range
app = Flask(__name__)
@app.before_request
def reject_private_ips():
remote_addr = request.remote_addr or ""
# Reject requests that resolve to private ranges after potential rebinding
private_ranges = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
if any(is_ip_in_range(remote_addr, net) for net in private_ranges):
abort(403, "Private IP access not allowed")
@app.route("/public")
def public():
return "Public endpoint"
For HTTP clients within Python code, avoid using user-supplied URLs directly; instead, maintain an allowlist of trusted hosts and resolve names to expected IPs before connecting.
Use reverse proxy controls and headers
Place Flask behind a reverse proxy (e.g., Nginx, Traefik) and configure the proxy to drop dangerous headers and enforce strict host checks. The proxy should not forward requests with X-Forwarded-Host that would cause the app to misinterpret the intended host.
# Example Nginx snippet (not Python, but part of a secure deployment)
# server {
# listen 80;
# server_name api.example.com;
# location / {
# proxy_pass http://127.0.0.1:5000;
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# }
# }
Leverage middleBrick for continuous verification
Use the middleBrick CLI to scan your Flask endpoints from the terminal and integrate scans into your workflow. For ongoing assurance, the Pro plan enables continuous monitoring and can be configured to alert when risk scores degrade. The GitHub Action can gate merges if scans exceed your security threshold, while the MCP Server lets you scan APIs directly from your Python IDE as part of development.
# Example CLI usage
middlebrick scan https://your-flask-api.example.com/openapi.json