Dictionary Attack in Flask with Basic Auth
Dictionary Attack in Flask with Basic Auth — how this specific combination creates or exposes the vulnerability
A dictionary attack against a Flask service using HTTP Basic Authentication attempts to gain access by systematically submitting common or leaked username and password combinations. Because Basic Auth encodes credentials only with Base64 and does not encrypt them, the transport confidentiality requirement immediately fails without TLS. In Flask, developers sometimes add a simple login_required wrapper that checks request.authorization, but if the underlying credential store or comparison logic is weak, the endpoint becomes susceptible to automated guessing.
Attack surface exposed by this combination includes:
- No built-in rate limiting in Flask itself means an attacker can send many authorization attempts per minute from a single source.
- Static or predictable usernames (e.g., admin, root, test) paired with common passwords found in breached lists increase the likelihood of successful compromise.
- Basic Auth transmits credentials on every request; without TLS, credentials are trivially intercepted even before any dictionary logic is applied.
- If Flask responses do not consistently enforce authentication and return different HTTP status codes or timing for missing versus incorrect credentials, attackers can perform username enumeration alongside password guessing.
During a scan, middleBrick tests for authentication weaknesses including dictionary-prone behaviors under the Authentication and BOLA/IDOR checks. It inspects whether responses reveal subtle timing differences or status code variations that could assist an attacker in narrowing valid credentials. Even in unauthenticated scans, the tool can detect whether TLS is enforced and whether rate limiting is present, which are prerequisites to mitigating dictionary attacks in Basic Auth scenarios.
Real-world attack patterns that map to this risk include credential stuffing using lists of breached passwords and usernames, as well as targeted brute forcing when username patterns are known. These behaviors are relevant to the OWASP API Top 10 under Broken Object Level Authorization and Credential Stuffing vectors. A Flask route that relies solely on Basic Auth without additional protections effectively places a static credential gate that can be bypassed given enough guesses and insufficient monitoring.
Basic Auth-Specific Remediation in Flask — concrete code fixes
Remediation focuses on replacing naive username/password checks with strong verification, enforcing TLS, and adding operational protections such as rate limiting. The following examples show a hardened approach while still using HTTP Basic Auth semantics.
First, always enforce TLS in production so credentials are not exposed in transit. Flask-Talisman can set HTTP Strict Transport Security headers.
from flask import Flask, request, Response
from flask_talisman import Talisman
import hashlib
import hmac
import os
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'fallback-secret-change-in-prod')
Talisman(app, force_https=True)
# Example secure credential store: use environment variables or a secrets manager
# In real deployments, store only salted hashes, not plain passwords
USERS = {
'admin': {
'salt': os.environ.get('ADMIN_SALT', 'replace-with-random'),
'hash': os.environ.get('ADMIN_HASH') # precomputed hash of password
}
}
def verify_password(username, password):
user = USERS.get(username)
if not user:
return False
# Constant-time comparison to reduce timing side-channels
expected = user['hash']
attempt = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), user['salt'].encode('utf-8'), 100000).hex()
return hmac.compare_digest(expected, attempt)
@app.before_request
def require_auth():
if request.path.startswith('/public'):
return
auth = request.authorization
if not auth or not verify_password(auth.username, auth.password):
return Response(
'Could not verify your access level for that URL.',
401,
{'WWW-Authenticate': 'Basic realm="Login Required"'}
)
@app.route('/api/protected')
def protected():
return {'status': 'ok'}
if __name__ == '__main__':
# Use a production WSGI server and TLS termination in real deployments
app.run(ssl_context='adhoc')
This example replaces plaintext password comparisons with salted, iterated hashing and uses constant-time comparison to reduce timing leakage. In a deployment managed by middleBrick Pro, continuous monitoring would alert you if response characteristics suggest authentication bypass attempts. For teams needing CI/CD enforcement, the GitHub Action can fail builds when risk scores exceed your defined thresholds, ensuring that new changes do not reintroduce weak authentication patterns.
Additionally, consider moving away from Basic Auth in favor of token-based mechanisms or session cookies with strong protections. If you must use Basic Auth, ensure that usernames are not predictable and that account lockout or throttling is enforced at the infrastructure level, as Flask itself does not provide these features natively. The MCP Server integration allows you to scan APIs directly from your AI coding assistant, helping catch misconfigurations before code reaches production.