HIGH broken authenticationflask

Broken Authentication in Flask

How Broken Authentication Manifests in Flask

Broken authentication in Flask applications typically emerges through several Flask-specific patterns. The most common vulnerability occurs when developers rely on Flask's default session management without proper configuration. Flask sessions are signed but not encrypted by default, meaning session data can be read if the secret key is compromised.

A classic Flask authentication flaw involves session fixation attacks. Consider this vulnerable pattern:

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    
    user = User.query.filter_by(username=username).first()
    if user and user.check_password(password):
        session['user_id'] = user.id
        return redirect('/dashboard')
    return 'Invalid credentials'

This code fails to regenerate the session ID after login, allowing attackers to fixate a session before authentication and gain access once the victim logs in.

Another Flask-specific issue involves improper use of flask-login's login_user() without proper session protection:

@app.route('/login')
def login():
    user = User.query.filter_by(username='admin').first()
    login_user(user, remember=True)
    return redirect('/admin')

The remember=True parameter creates persistent cookies without adequate protection against theft. Flask's default session cookie (session) is vulnerable if transmitted over HTTP or if the secret key is weak.

Flask's before_request decorators can also introduce authentication bypass if not implemented correctly:

@app.before_request
def check_auth():
    if request.endpoint != 'login':
        if 'user_id' not in session:
            return redirect('/login')
    # Missing return for authenticated case!

This function returns None for authenticated requests, which Flask treats as continuing execution, but the logic is confusing and error-prone. A more subtle issue occurs with Flask's abort() function:

@app.route('/admin')
def admin():
    if not session.get('is_admin'):
        abort(403)
    # Attacker can trigger abort handlers that reveal system information

Improper error handling in abort handlers can leak authentication bypass opportunities through timing differences or error messages.

Flask-Specific Detection

Detecting broken authentication in Flask requires examining both the application code and runtime behavior. For code analysis, look for these Flask-specific patterns:

Session Management Issues: Search for app.secret_key assignments with weak values or hardcoded secrets. Flask sessions should use strong, randomly generated keys of at least 24 bytes.

Missing Session Regeneration: After successful authentication, session IDs must be regenerated. Use session.regenerate() or implement session rotation manually.

from flask import session
from flask import redirect, url_for
from flask import request

@app.route('/secure-login', methods=['POST'])
def secure_login():
    # ... authentication logic ...
    if authenticated:
        # Regenerate session to prevent fixation
        session.regenerate()
        session['user_id'] = user.id
        return redirect(url_for('dashboard'))

CSRF Protection Gaps: Flask-WTF provides CSRF protection, but it must be explicitly enabled:

from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

# In templates: {{ form.csrf_token }}

Authentication Bypass via Route Order: Flask processes routes in the order they're defined. A catch-all route before an authenticated route can bypass protection:

@app.route('/')
def catch_all(path):
    return 'Hello!'

@app.route('/admin')
def admin():
    # This is never reached if catch_all matches first
    return 'Admin panel'

Runtime Detection with middleBrick: middleBrick's black-box scanning can identify Flask authentication issues without access to source code. The scanner tests for common Flask vulnerabilities like session fixation by attempting to reuse session cookies across authentication boundaries. It also checks for missing CSRF tokens by submitting forms without the expected csrf_token field and observing whether the server accepts the request.

middleBrick specifically tests Flask's default session behavior by manipulating the session cookie and observing how the application responds to session tampering attempts. The scanner's authentication bypass checks include testing for predictable session token generation and insufficient session validation logic.

Flask-Specific Remediation

Securing Flask authentication requires leveraging Flask's built-in security features and following established patterns. Here's a comprehensive approach to fixing broken authentication in Flask:

Strong Session Configuration:

from flask import Flask, session
import os

app = Flask(__name__)
app.secret_key = os.environ.get('FLASK_SECRET_KEY') or os.urandom(24)

# Use secure session configuration
app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SECURE=True,  # HTTPS only
    SESSION_COOKIE_SAMESITE='Lax',
    PERMANENT_SESSION_LIFETIME=timedelta(hours=1)
)

Proper Authentication Flow:

from flask import session, redirect, url_for
from flask import request
from werkzeug.security import check_password_hash

@app.route('/login', methods=['POST'])
def login():
    username = request.form['username']
    password = request.form['password']
    
    user = User.query.filter_by(username=username).first()
    if user and check_password_hash(user.password_hash, password):
        # Regenerate session to prevent fixation
        session.regenerate()
        session['user_id'] = user.id
        session['authenticated'] = True
        session['csrf_token'] = os.urandom(16).hex()
        return redirect(url_for('dashboard'))
    return 'Invalid credentials', 401

@app.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('login'))

CSRF Protection Implementation:

from flask_wtf.csrf import CSRFProtect
from flask import request

csrf = CSRFProtect(app)

@app.route('/submit-form', methods=['POST'])
def submit_form()):
    if not csrf.validate_csrf(request.form.get('csrf_token')):
        return 'CSRF validation failed', 403
    # Process form data

Role-Based Access Control:

from functools import wraps

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not session.get('authenticated'):
            return redirect(url_for('login'))
        return f(*args, **kwargs)
    return decorated_function

def role_required(allowed_roles):
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            user_role = session.get('user_role')
            if user_role not in allowed_roles:
                return 'Insufficient permissions', 403
            return f(*args, **kwargs)
        return decorated_function
    return decorator

@app.route('/admin')
@login_required
@role_required(['admin'])
def admin():
    return 'Admin panel'

Rate Limiting for Authentication:

from flask import request
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route('/login', methods=['POST'])
@limiter.limit("5/minute;20/hour")
def login():
    # Authentication logic with rate limiting

Secure Password Handling:

from werkzeug.security import generate_password_hash, check_password_hash

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(128))
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

These remediation strategies address the most common Flask authentication vulnerabilities. For comprehensive security validation, scanning your Flask API with middleBrick can identify authentication weaknesses that might be missed during manual code review.

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How does Flask's default session management create security risks?
Flask sessions are signed but not encrypted by default, making them readable if the secret key is compromised. Additionally, Flask doesn't automatically regenerate session IDs after authentication, leaving applications vulnerable to session fixation attacks. The default session cookie can also be transmitted over HTTP if not properly configured with SECURE and HTTPONLY flags.
What makes middleBrick's Flask authentication scanning unique?
middleBrick actively tests Flask-specific authentication patterns including session fixation attempts, CSRF bypass scenarios, and authentication bypass through route manipulation. Unlike static analysis tools, middleBrick performs black-box scanning that identifies runtime authentication vulnerabilities by interacting with your Flask application as an attacker would, testing for predictable session token generation and insufficient session validation logic.