Dns Rebinding in Flask with Cockroachdb
Dns Rebinding in Flask with Cockroachdb — how this specific combination creates or exposes the vulnerability
DNS rebinding occurs when an attacker forces a victim’s browser to resolve a hostname to an IP address under the attacker’s control, then switches the resolution to a different target, often an internal system. In a Flask application that interacts with CockroachDB, this can expose the database if the application or its dependencies trust hostnames or IPs in a way that bypasses intended network boundaries.
Consider a Flask service that connects to CockroachDB using a hostname like cockroach.local or a service name that resolves differently depending on network context. If the Flask app resolves the database hostname once at startup or per request without revalidating the resolved IP, an attacker on the same network or via a malicious site can manipulate DNS to point cockroach.local first to a public address under their control, then to 127.0.0.1 or 192.168.1.10. When the Flask app later uses the same connection or reuses a connection pool entry, it may inadvertently route database queries through an unintended path, potentially reaching an internal CockroachDB node that would not be exposed directly to the web.
In a typical CockroachDB deployment, nodes communicate using internal hostnames and require strict network access controls. If Flask’s database client is configured to trust the resolved hostname without additional verification, DNS rebinding can subvert those controls. For example, an attacker serving a page with fetch('http://flask-app.example.com/api/query') can trigger requests that cause Flask to interact with CockroachDB via a connection string that appears valid but is redirected internally. This is particularly relevant when Flask uses environment variables or configuration files that specify COCKROACH_HOST or similar, and those values are not rechecked against a strict allowlist of IPs or network segments after resolution.
Moreover, if the Flask app exposes an endpoint that accepts database host or port parameters and passes them directly to the CockroachDB driver without validation, DNS rebinding becomes a practical attack vector. The attacker can craft a URL like https://flask-app.example.com/query?host=cockroach.example.com, where cockroach.example.com is controlled after initial resolution. The Flask app, using a driver such as cockroachdb or sqlalchemy with a dynamic connection URI, may open a connection to the attacker-controlled host or, if the hostname resolves internally, to a sensitive internal address.
middleBrick can detect such misconfigurations during unauthenticated scans by analyzing how the API surface interacts with external services and whether findings expose internal endpoints. While the scanner does not fix these issues, it provides prioritized findings with remediation guidance, helping teams understand where hostname validation and network segmentation are insufficient.
Cockroachdb-Specific Remediation in Flask — concrete code fixes
To mitigate DNS rebinding in Flask when using CockroachDB, enforce strict hostname and IP validation at the point where database connections are created or reused. Avoid dynamic resolution of database hosts from user input, and if configuration values are used, validate them against a strict allowlist of IPs or CIDR ranges before passing them to the CockroachDB driver.
Use a static connection string for CockroachDB in your Flask configuration and ensure that any parameters used to construct the connection are not derived from untrusted sources. For example, instead of allowing a user-supplied host, hardcode or securely inject the CockroachDB endpoint.
import CockroachDBClient
from flask import Flask, request, jsonify
app = Flask(__name__)
# Safe: static, validated connection parameters
COCKROACH_HOST = '10.1.2.3' # Private IP within your network
COCKROACH_PORT = 26257
COCKROACH_USER = 'app_user'
COCKROACH_PASSWORD = 'secure_password'
COCKROACH_DATABASE = 'app_db'
# Construct connection string from trusted constants
DATABASE_URI = (
f'cockroachdb://{COCKROACH_USER}:{COCKROACH_PASSWORD}@'
f'{COCKROACH_HOST}:{COCKROACH_PORT}/{COCKROACH_DATABASE}'
)
def get_db_client():
return CockroachDBClient.connect(DATABASE_URI)
@app.route('/api/data')
def query_data():
db = get_db_client()
cursor = db.cursor()
cursor.execute('SELECT id, name FROM products WHERE category = $1', ('electronics',))
results = cursor.fetchall()
return jsonify([dict(row) for row in results])
If you must accept a hostname or port, validate them strictly. For instance, allow only hostnames from a predefined set or IPs within specific subnets using libraries such as ipaddress:
import ipaddress
from flask import abort
ALLOWED_SUBNET = ipaddress.ip_network('10.1.2.0/24')
def validate_db_target(host, port):
try:
ip = ipaddress.ip_address(host)
if ip not in ALLOWED_SUBNET:
return False
except ValueError:
# Optionally allow a small set of hostnames with strict DNS pinning
if host not in ('cockroach-internal.example.com',):
return False
if not (1024 <= port <= 65535):
return False
return True
@app.route('/api/query')
def safe_query():
host = request.args.get('host')
port = int(request.args.get('port', 26257))
if not validate_db_target(host, port):
abort(400, 'Invalid database target')
uri = f'cockroachdb://{host}:{port}/app_db'
db = CockroachDBClient.connect(uri)
# proceed with query
return jsonify({'status': 'ok'})
Additionally, configure your CockroachDB cluster to listen only on internal interfaces and enforce firewall rules that prevent external access. In your Flask deployment, ensure that DNS resolution is not performed at runtime for database endpoints; prefer static IPs or tightly controlled internal hostnames with short TTLs. These steps reduce the attack surface for DNS rebinding and help ensure that only intended CockroachDB nodes are reachable from your application.