HIGH dictionary attackflask

Dictionary Attack in Flask

How Dictionary Attack Manifests in Flask

A dictionary attack systematically attempts common passwords against an authentication endpoint, exploiting weak credential policies or missing rate controls. In Flask applications, this vulnerability typically arises from two intertwined oversights: inadequate password storage and absent request throttling.

Flask does not enforce password hashing or rate limiting by default. A vulnerable login route often stores passwords in plaintext or with weak hashes (e.g., MD5) and lacks any limit on failed attempts:

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    password = db.Column(db.String(120))  # Plaintext or weak hash!

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    user = User.query.filter_by(username=username).first()
    # VULNERABLE: Plaintext comparison
    if user and user.password == password:
        return {'status': 'success'}
    return {'error': 'Invalid credentials'}, 401

Without rate limiting, an attacker can script thousands of guesses per minute using tools like hydra or burp suite against /login. Even with hashed passwords, weak hashes (e.g., SHA-1) enable offline cracking. Flask's flexibility means developers must explicitly add protections; the framework itself provides no built-in brute-force defense.

Another Flask-specific pattern is exposing JWT issuance without throttling. If using flask-jwt-extended, a /auth endpoint that issues tokens on valid credentials but lacks rate limits becomes a prime target. Attackers can flood this endpoint with dictionary passwords, potentially locking out users or exhausting server resources.

Flask-Specific Detection

Detecting dictionary attack exposure in Flask requires examining both code and runtime behavior. Start by auditing all authentication routes (/login, /auth, /signin) for two signals: (1) absence of rate limiting, and (2) insecure password handling.

Manual Code Review: Search for routes accepting POST credentials. Check if Flask-Limiter or similar decorators are applied. Verify password storage uses strong algorithms (bcrypt, Argon2). Red flags include:

  • No @limiter.limit decorator on login endpoints.
  • Direct password comparison (e.g., if user.password == password).
  • Use of hashlib.md5 or sha1 without salt.

Dynamic Scanning with middleBrick: Submit your Flask API's base URL to middleBrick. Its Rate Limiting check automatically sends repeated requests to credential-accepting endpoints and observes if responses become throttled (HTTP 429) or if error messages change after multiple failures. The scanner also cross-references any OpenAPI/Swagger spec to locate /login paths and test them. A missing rate limit triggers a high-severity finding under the OWASP API Top 10 A2: Broken Authentication category. The report includes the exact endpoint tested and evidence of unthrottled requests.

# Example middleBrick CLI scan
$ middlebrick scan https://api.example.com

# Output snippet (JSON):
{
  "score": 65,
  "category_breakdown": {
    "rate_limiting": {
      "severity": "high",
      "finding": "Login endpoint /v1/login accepts unlimited requests"
    }
  }
}

Complementary tools like OWASP ZAP can manually brute-force the endpoint, but middleBrick automates this as part of its 12 parallel checks, providing a reproducible risk score (0–100) and remediation guidance specific to Flask's ecosystem.

Flask-Specific Remediation

Fix dictionary attack exposure in Flask by implementing two layers: rate limiting and strong password hashing. Use battle-tested extensions rather than rolling your own.

1. Enforce Rate Limiting with Flask-Limiter: Install flask-limiter and apply it to all authentication routes. Use a key function that locks by username or IP to prevent credential stuffing across accounts:

from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
limiter = Limiter(
    app,
    key_func=get_remote_address,  # Or custom: lambda: request.form.get('username', get_remote_address())
    default_limits=["100 per day", "10 per minute"]
)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per 15 minutes", key_func=lambda: request.form.get('username', 'global'))
def login():
    # ... authentication logic
    return {'status': 'success'}

This limits each username to 5 attempts per 15 minutes. Adjust limits based on your risk tolerance. The key_func ensures a single user's failures don't block others if using IP-based limits.

2. Hash Passwords with bcrypt: Never store plaintext. Use flask-bcrypt or passlib:

from flask_bcrypt import Bcrypt

bcrypt = Bcrypt(app)

# During user registration:
hashed_pw = bcrypt.generate_password_hash(password).decode('utf-8')
new_user = User(username=username, password=hashed_pw)

# During login:
if user and bcrypt.check_password_hash(user.password, password):
    # Success
    ...

3. Add Account Lockout (Optional but Recommended): Track failed attempts in your database and temporarily lock accounts after, say, 5 failures. This complements rate limiting but requires careful implementation to avoid denial-of-service via lockout.

After applying these fixes, rescan with middleBrick to confirm the Rate Limiting check passes and your security score improves. Remember: middleBrick only detects and reports; it does not modify your code. The remediation guidance it provides will point you to these Flask-specific patterns.

Frequently Asked Questions

How does a dictionary attack differ from credential stuffing in a Flask application?
A dictionary attack uses a list of common passwords (e.g., 'password123'), while credential stuffing uses passwords leaked from other breaches, relying on password reuse. Both target Flask login endpoints, but stuffing often has higher success rates. Flask apps without rate limiting are vulnerable to both; the mitigation is identical: throttle requests and enforce strong password policies.
Does Flask have built-in protection against dictionary attacks?
No. Flask does not include built-in rate limiting or secure password storage. Developers must add extensions like Flask-Limiter for throttling and Flask-Bcrypt for hashing. MiddleBrick can scan your Flask API to identify missing these protections and provide Flask-specific remediation steps.