HIGH dns rebindingflaskbasic auth

Dns Rebinding in Flask with Basic Auth

Dns Rebinding in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability

DNS rebinding is an application-layer attack that manipulates DNS responses to make a victim’s browser believe a remote host is reachable at an unintended IP address, often bypassing same-origin policies and network-based access controls. In a Flask application protected with HTTP Basic Authentication, combining weak host validation with Basic Auth creates a scenario where an authenticated session can be abused across network boundaries.

Consider a Flask route that performs internal checks or management operations. If the route trusts an incoming host header or uses the request’s host to construct URLs without strict validation, an attacker can use DNS rebinding to make the client send requests that resolve to internal addresses (e.g., 127.0.0.1 or internal services). Even when Basic Auth is required, if the client’s browser has already cached valid credentials for the domain, the rebinding request will carry those credentials automatically. This means the attacker can induce the victim’s browser to issue authenticated requests to internal endpoints that would otherwise be unreachable from the internet, effectively bypassing network segregation.

For example, an internal admin endpoint like /admin/health may be protected by Basic Auth but not intended for external access. An attacker crafts a page that causes the victim’s browser to resolve example.com to 127.0.0.1 and send an authenticated request to that endpoint. Flask’s default behavior does not inherently prevent this if the application does not explicitly validate the server name or origin. The attack chain is:

  1. User authenticates to example.com with Basic Auth, establishing credentials in the browser.
  2. User visits an attacker-controlled page that triggers DNS rebinding, making the browser send requests to an internal IP.
  3. Browser includes the cached Basic Auth credentials, granting access if the endpoint lacks additional authorization checks.

middleBrick detects this risk as part of its BOLA/IDOR and Property Authorization checks, mapping findings to OWASP API Top 10 and highlighting the need for explicit host validation and strict authorization boundaries.

Basic Auth-Specific Remediation in Flask — concrete code fixes

To mitigate DNS rebinding in Flask when using HTTP Basic Auth, you must enforce strict host validation and avoid implicit trust in request metadata. Below are concrete, secure patterns for Flask applications.

1. Explicitly validate the request host

Do not rely on the Host header. Compare the server name against a strict allowlist.

from flask import Flask, request, abort

app = Flask(__name__)

ALLOWED_HOSTS = {"api.example.com", "www.example.com"}

@app.before_request
def enforce_known_host():
    if request.host not in ALLOWED_HOSTS:
        abort(403, "Host not allowed")

2. Use a request decorator for Basic Auth with constant-time comparison

Use werkzeug.security.check_password_hash for password verification and avoid timing leaks. Define a helper to extract and validate credentials on each request.

from flask import Flask, request, Response, abort
from werkzeug.security import generate_password_hash, check_password_hash

app = Flask(__name__)

USERS = {
    "admin": generate_password_hash("strong-secret"),
}

def check_auth(username, password):
    if username not in USERS:
        return False
    return check_password_hash(USERS[username], password)

def authenticate():
    return Response(
        "Authentication required",
        401,
        {"WWW-Authenticate": "Basic realm="\"Login Required\"""},
    )

@app.before_request
def require_auth():
    auth = request.authorization
    if not auth or not check_auth(auth.username, auth.password):
        return authenticate()

3. Do not use the request host for internal redirects or URL construction

When returning URLs or constructing links, use a configured base URL rather than request.host or request.url.

from flask import url_for

@app.route("/safe-endpoint")
def safe_endpoint():
    # Use url_for with explicit endpoint names instead of dynamic host-based URLs
    return {"status": "ok", "self": url_for("safe_endpoint", _external=True)}

4. Combine with route-level authorization and scope checks

Even with Basic Auth, enforce role-based or ownership checks on sensitive endpoints to limit the impact of any credential bypass.

from flask import g

@app.route("/admin/health")
def admin_health():
    if not getattr(g, "user_role", None) == "admin":
        abort(403, "Insufficient permissions")
    return {"status": "healthy"}

By explicitly validating the server name, using constant-time credential checks, and avoiding host-derived routing, you reduce the attack surface for DNS rebinding while maintaining Basic Auth usability. middleBrick’s Authorization and Property checks can validate these controls in runtime scans to ensure compliance with security best practices.

Frequently Asked Questions

Does HTTP Basic Auth alone prevent DNS rebinding attacks in Flask?
No. Basic Auth provides credential transport but does not prevent the browser from sending those credentials to a rebinded host. You must implement explicit host validation and strict authorization boundaries.
How can I test that my Flask Basic Auth setup is resilient to DNS rebinding?
Use a local resolver or hosts file to redirect your domain to 127.0.0.1 and verify that endpoints reject requests when the Host header does not match an allowlist. middleBrick can scan your endpoint to detect missing host validation.