HIGH cross site request forgeryflask

Cross Site Request Forgery in Flask

How Cross Site Request Forgery Manifests in Flask

Cross Site Request Forgery (CSRF) in Flask applications occurs when an attacker tricks a victim into executing unwanted actions on a web application where they're authenticated. Flask's default behavior makes it particularly vulnerable to CSRF attacks because it doesn't implement any built-in CSRF protection mechanisms.

The most common CSRF attack pattern in Flask involves state-changing operations that rely solely on cookies for authentication. Consider this Flask endpoint:

@app.route('/transfer', methods=['POST'])
def transfer():
    if request.method == 'POST':
        amount = request.form.get('amount')
        target = request.form.get('target')
        
        # No CSRF protection
        if transfer_funds(amount, target):
            return 'Transfer successful'
        return 'Transfer failed'

An attacker can create a malicious page that automatically submits this form when loaded:

<form action="https://your-flask-app.com/transfer" method="POST" id="csrf-form" style="display:none;">
    <input type="hidden" name="amount" value="1000">
    <input type="hidden" name="target" value="attacker_account">
</form>
<script>
    document.getElementById('csrf-form').submit();
</script>

When a victim visits this malicious page while logged into the Flask application, their browser automatically includes their session cookie with the POST request, causing the transfer to execute without their knowledge or consent.

Flask's session management exacerbates this vulnerability. By default, Flask uses signed cookies for sessions, which means the server trusts any request with a valid session cookie. This trust model makes CSRF particularly dangerous because the attacker doesn't need to know the session ID—they just need the victim to be authenticated.

Another Flask-specific CSRF pattern involves API endpoints that accept JSON payloads without proper authentication headers. Many developers create Flask endpoints like:

@app.route('/api/update_profile', methods=['POST'])
def update_profile():
    data = request.get_json()
    
    # Updates profile using session cookie only
    user = get_current_user()
    user.update(data)
    return jsonify(success=True)

These endpoints are vulnerable because modern browsers can make cross-origin requests with custom headers, and if the server doesn't implement CSRF tokens or same-origin policies, the request will be processed.

Flask applications that use Flask-Login for authentication are also vulnerable to CSRF because Flask-Login only manages user sessions—it doesn't provide CSRF protection. The combination of session-based authentication and state-changing endpoints creates the perfect CSRF vulnerability.

Flask-Specific Detection

Detecting CSRF vulnerabilities in Flask applications requires both static code analysis and dynamic testing. From a static perspective, you can identify vulnerable patterns by examining your Flask routes and identifying state-changing operations that lack CSRF protection.

Look for these Flask-specific patterns in your codebase:

# Vulnerable patterns
@app.route('/delete_account', methods=['POST'])
def delete_account():
    # No CSRF token validation
    if request.method == 'POST':
        delete_user()
        return redirect('/login')

@app.route('/api/change_password', methods=['PUT'])
def change_password():
    # No authentication header validation
    data = request.get_json()
    user = get_current_user()  # Relies on session cookie
    user.password = data['password']
    return jsonify(success=True)

Dynamic detection with middleBrick specifically tests Flask applications by attempting to execute state-changing operations without proper authentication tokens. The scanner identifies endpoints that accept POST/PUT/DELETE requests and attempts to trigger these operations from different origins.

middleBrick's CSRF detection for Flask applications includes:

  • Identifying endpoints that modify state without CSRF tokens
  • Testing whether session-based authentication alone is sufficient for state changes
  • Checking for proper SameSite cookie attributes on session cookies
  • Verifying that JSON API endpoints require authentication headers beyond session cookies

The scanner also tests for Flask-specific CSRF bypass techniques, such as exploiting endpoints that accept form-encoded data versus JSON, or testing whether certain HTTP methods are improperly allowed without CSRF protection.

For Flask applications using Flask-WTF, middleBrick verifies that CSRFProtect is properly configured and that all relevant forms include CSRF tokens. The scanner checks for common misconfigurations like:

# Potentially misconfigured
csrf = CSRFProtect(app)
csrf.init_app(app)  # Missing this line is a common mistake

middleBrick's scanning process for Flask applications takes approximately 5-15 seconds and provides a security score with specific findings about CSRF vulnerabilities, including the exact endpoints affected and the severity of each finding.

Flask-Specific Remediation

Flask provides several approaches to mitigate CSRF vulnerabilities, with the most robust being the Flask-WTF extension's CSRF protection. Here's how to properly implement CSRF protection in Flask applications:

from flask_wtf.csrf import CSRFProtect
from flask_wtf import FlaskForm
from wtforms import SubmitField

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'
app.config['WTF_CSRF_SECRET_KEY'] = 'unique-csrf-key'

# Initialize CSRF protection
csrf = CSRFProtect(app)
csrf.init_app(app)  # Critical initialization step

class TransferForm(FlaskForm):
    amount = IntegerField('Amount')
    target = StringField('Target Account')
    submit = SubmitField('Transfer')

@app.route('/transfer', methods=['GET', 'POST'])
def transfer():
    form = TransferForm()
    if form.validate_on_submit():
        amount = form.amount.data
        target = form.target.data
        
        # CSRF token automatically validated by Flask-WTF
        if transfer_funds(amount, target):
            return 'Transfer successful'
        return 'Transfer failed'
    return render_template('transfer.html', form=form)

For API endpoints that use JSON, implement token-based authentication instead of relying on session cookies:

from flask import request, jsonify
from functools import wraps

def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('x-access-token')
        if not token:
            return jsonify({'message': 'Token is missing'}), 401
        
        # Validate token
        if not validate_token(token):
            return jsonify({'message': 'Token is invalid'}), 401
        
        return f(*args, **kwargs)
    return decorated

@app.route('/api/update_profile', methods=['PUT'])
@token_required
def update_profile():
    data = request.get_json()
    user = get_user_from_token()
    user.update(data)
    return jsonify(success=True)

For applications that must support both traditional web forms and APIs, implement a hybrid approach:

from flask import request

def csrf_protected():
    if request.content_type == 'application/x-www-form-urlencoded':
        # Validate CSRF token for form submissions
        token = request.form.get('csrf_token')
        if not validate_csrf_token(token):
            return jsonify(error='CSRF token invalid'), 403
    
    # For JSON APIs, rely on token authentication
    if request.content_type == 'application/json':
        token = request.headers.get('Authorization')
        if not validate_token(token):
            return jsonify(error='Authentication required'), 401

def my_endpoint():
    csrf_protected()
    # Process request

Additionally, configure your Flask application's session cookies with proper security attributes:

app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'

These settings prevent cookies from being sent in cross-site contexts and protect against various CSRF and XSS attacks.

Frequently Asked Questions

Does Flask have built-in CSRF protection?
No, Flask does not include built-in CSRF protection. Developers must implement CSRF protection using extensions like Flask-WTF or by manually validating CSRF tokens. This is a common security oversight in Flask applications.
How does middleBrick detect CSRF vulnerabilities in Flask applications?
middleBrick detects CSRF vulnerabilities by identifying state-changing endpoints that lack proper CSRF protection, testing whether session cookies alone can trigger state changes, and verifying that API endpoints require authentication headers beyond session cookies. The scanner provides specific findings about vulnerable endpoints and their severity.