CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wtforms

Form validation and rendering for Python web development.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

csrf.mddocs/

CSRF Protection

Built-in cross-site request forgery protection with session-based token generation and validation, configurable through form meta options. WTForms provides a comprehensive CSRF protection system that integrates seamlessly with web frameworks.

Capabilities

CSRF Field Types

Special field types for handling CSRF tokens.

class CSRFTokenField(HiddenField):
    """
    Hidden field for CSRF tokens.
    Automatically renders current token and never populates object data.
    Extends HiddenField with CSRF-specific behavior.
    """
    def __init__(self, label=None, validators=None, **kwargs): ...
    
    def populate_obj(self, obj, name):
        """Override to prevent populating object attributes."""
        pass

Core CSRF Classes

Abstract base and concrete implementations for CSRF protection.

class CSRF:
    """
    Abstract base class for CSRF protection implementations.
    Defines the interface for CSRF token generation and validation.
    """
    def setup_form(self, form) -> list:
        """
        Add CSRF fields to form.
        
        Parameters:
        - form: Form instance to protect
        
        Returns:
        list: List of (field_name, unbound_field) tuples to add to form
        """
    
    def generate_csrf_token(self, csrf_token_field) -> str:
        """
        Generate CSRF token (abstract method).
        
        Parameters:
        - csrf_token_field: CSRFTokenField instance
        
        Returns:
        str: Generated CSRF token
        """
        raise NotImplementedError()
    
    def validate_csrf_token(self, form, field):
        """
        Validate CSRF token (abstract method).
        
        Parameters:
        - form: Form instance
        - field: CSRFTokenField instance
        
        Raises:
        ValidationError: If token is invalid
        """
        raise NotImplementedError()

class SessionCSRF(CSRF):
    """
    Session-based CSRF implementation using HMAC-SHA1.
    Stores token data in session and validates against generated tokens.
    
    Parameters:
    - secret_key: Secret key for HMAC generation (bytes)
    - timeout: Token timeout in seconds (None for no timeout)
    """
    def __init__(self, secret_key, timeout=None): ...
    
    def generate_csrf_token(self, csrf_token_field) -> str:
        """
        Generate HMAC-SHA1 based CSRF token.
        
        Returns:
        str: Base64 encoded token with timestamp and hash
        """
    
    def validate_csrf_token(self, form, field):
        """
        Validate CSRF token against session data and timestamp.
        
        Raises:
        ValidationError: If token is missing, expired, or invalid
        """

CSRF Usage Examples

Basic CSRF Protection

from wtforms import Form, StringField, validators
from wtforms.csrf.session import SessionCSRF
from wtforms.meta import DefaultMeta

class SecureForm(Form):
    class Meta(DefaultMeta):
        csrf = True
        csrf_secret = b'your-secret-key-here'  # Must be bytes
        csrf_context = session  # Session-like object (Flask session, etc.)
        csrf_field_name = 'csrf_token'  # Default field name
    
    message = StringField('Message', [validators.DataRequired()])

# Usage with Flask
@app.route('/contact', methods=['GET', 'POST'])
def contact():
    form = SecureForm(formdata=request.form, meta={'csrf_context': session})
    
    if form.validate():
        # Form passed CSRF validation
        process_message(form.message.data)
        return redirect('/success')
    
    return render_template('contact.html', form=form)

Template Usage

<!-- In your HTML template -->
<form method="POST">
    {{ form.csrf_token }}  <!-- Hidden CSRF field -->
    {{ form.message.label }} {{ form.message() }}
    <input type="submit" value="Submit">
</form>

<!-- Or explicitly render CSRF token -->
<form method="POST">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
    <!-- Other form fields -->
</form>

Custom CSRF Configuration

from wtforms.csrf.session import SessionCSRF

class CustomCSRFForm(Form):
    class Meta:
        csrf = True
        csrf_secret = b'super-secret-key'
        csrf_field_name = 'authenticity_token'  # Custom field name
        csrf_context = None  # Will be set per request
        csrf_class = SessionCSRF  # Explicit CSRF class
        
    name = StringField('Name')
    email = StringField('Email')

# Per-request context setting
def create_form(request):
    form = CustomCSRFForm(
        formdata=request.form,
        meta={'csrf_context': request.session}
    )
    return form

CSRF with Timeout

from wtforms.csrf.session import SessionCSRF

class TimedCSRFForm(Form):
    class Meta:
        csrf = True
        csrf_secret = b'your-secret-key'
        csrf_context = session
        
    # Custom CSRF with 30 minute timeout
    @property
    def csrf_class(self):
        return SessionCSRF(secret_key=self.Meta.csrf_secret, timeout=1800)
    
    data = StringField('Data')

# Tokens will expire after 30 minutes
form = TimedCSRFForm(formdata=request.form)
if form.validate():
    # Process valid form with non-expired token
    pass

Multiple Forms with CSRF

class LoginForm(Form):
    class Meta:
        csrf = True
        csrf_secret = b'login-secret'
        csrf_field_name = 'login_token'
        csrf_context = session
    
    username = StringField('Username')
    password = StringField('Password')

class RegistrationForm(Form):
    class Meta:
        csrf = True
        csrf_secret = b'register-secret'  # Different secret
        csrf_field_name = 'register_token'  # Different field name
        csrf_context = session
    
    username = StringField('Username')
    email = StringField('Email')
    password = StringField('Password')

# Both forms can coexist with different CSRF tokens
@app.route('/auth', methods=['GET', 'POST'])
def auth():
    login_form = LoginForm(formdata=request.form, prefix='login')
    register_form = RegistrationForm(formdata=request.form, prefix='register')
    
    if 'login_submit' in request.form and login_form.validate():
        # Process login
        pass
    elif 'register_submit' in request.form and register_form.validate():
        # Process registration
        pass
    
    return render_template('auth.html', 
                         login_form=login_form, 
                         register_form=register_form)

Custom CSRF Implementation

from wtforms.csrf.core import CSRF, CSRFTokenField
from wtforms.validators import ValidationError
import hmac
import hashlib
import time
import base64

class DatabaseCSRF(CSRF):
    """Custom CSRF implementation using database storage."""
    
    def __init__(self, secret_key, db_session, timeout=3600):
        self.secret_key = secret_key
        self.db_session = db_session
        self.timeout = timeout
    
    def generate_csrf_token(self, csrf_token_field):
        # Generate unique token
        timestamp = str(int(time.time()))
        user_id = str(getattr(csrf_token_field.form, 'current_user_id', 0))
        
        # Create token data
        token_data = f"{timestamp}:{user_id}"
        signature = hmac.new(
            self.secret_key,
            token_data.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        
        token = base64.b64encode(f"{token_data}:{signature}".encode()).decode()
        
        # Store in database
        csrf_record = CSRFToken(token=token, created_at=time.time())
        self.db_session.add(csrf_record)
        self.db_session.commit()
        
        return token
    
    def validate_csrf_token(self, form, field):
        if not field.data:
            raise ValidationError('CSRF token missing')
        
        try:
            # Decode token
            decoded = base64.b64decode(field.data).decode()
            timestamp, user_id, signature = decoded.split(':')
            
            # Verify signature
            expected_data = f"{timestamp}:{user_id}"
            expected_signature = hmac.new(
                self.secret_key,
                expected_data.encode('utf-8'),
                hashlib.sha256
            ).hexdigest()
            
            if not hmac.compare_digest(signature, expected_signature):
                raise ValidationError('Invalid CSRF token')
            
            # Check timeout
            if time.time() - int(timestamp) > self.timeout:
                raise ValidationError('CSRF token expired')
            
            # Verify token exists in database
            csrf_record = self.db_session.query(CSRFToken).filter_by(
                token=field.data
            ).first()
            
            if not csrf_record:
                raise ValidationError('CSRF token not found')
            
            # Remove used token (one-time use)
            self.db_session.delete(csrf_record)
            self.db_session.commit()
            
        except (ValueError, TypeError):
            raise ValidationError('Malformed CSRF token')

class DatabaseProtectedForm(Form):
    class Meta:
        csrf = True
        csrf_class = DatabaseCSRF
        csrf_secret = b'database-csrf-secret'
        
    def __init__(self, db_session, *args, **kwargs):
        # Inject database session into CSRF
        meta = kwargs.get('meta', {})
        meta['csrf_class'] = DatabaseCSRF(
            secret_key=self.Meta.csrf_secret,
            db_session=db_session
        )
        kwargs['meta'] = meta
        super().__init__(*args, **kwargs)
    
    data = StringField('Data')

CSRF Error Handling

from wtforms.validators import ValidationError

class CSRFProtectedForm(Form):
    class Meta:
        csrf = True
        csrf_secret = b'secret-key'
        csrf_context = session
    
    message = StringField('Message')

@app.route('/submit', methods=['POST'])
def submit():
    form = CSRFProtectedForm(formdata=request.form)
    
    try:
        if form.validate():
            # Process valid form
            return jsonify({'status': 'success'})
        else:
            # Handle validation errors
            errors = {}
            for field_name, field_errors in form.errors.items():
                if field_name == 'csrf_token':
                    # CSRF-specific error handling
                    return jsonify({
                        'status': 'error',
                        'message': 'Security token invalid. Please refresh and try again.'
                    }), 403
                errors[field_name] = field_errors
            
            return jsonify({'status': 'error', 'errors': errors}), 400
            
    except ValidationError as e:
        # Handle CSRF validation exceptions
        return jsonify({
            'status': 'error',
            'message': 'Security validation failed'
        }), 403

CSRF with AJAX

# Server-side: Provide CSRF token for AJAX requests
@app.route('/api/csrf-token')
def csrf_token():
    form = CSRFProtectedForm()  # Create form to get token
    return jsonify({'csrf_token': form.csrf_token.data})

# Client-side JavaScript
fetch('/api/csrf-token')
    .then(response => response.json())
    .then(data => {
        // Include CSRF token in subsequent requests
        fetch('/api/submit', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-Token': data.csrf_token
            },
            body: JSON.stringify({message: 'Hello World'})
        });
    });

Framework Integration Examples

Flask Integration

from flask import Flask, session, request
from wtforms.csrf.session import SessionCSRF

app = Flask(__name__)
app.secret_key = 'your-flask-secret'

class FlaskForm(Form):
    class Meta:
        csrf = True
        csrf_secret = app.secret_key.encode()
        csrf_context = session
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

# Usage
@app.route('/form', methods=['GET', 'POST'])
def form_view():
    form = FlaskForm(formdata=request.form)
    if form.validate():
        # Process form
        pass
    return render_template('form.html', form=form)

Django Integration

from django.middleware.csrf import get_token

class DjangoForm(Form):
    class Meta:
        csrf = True
        csrf_secret = settings.SECRET_KEY.encode()
        
    def __init__(self, request, *args, **kwargs):
        # Use Django's CSRF token
        meta = kwargs.get('meta', {})
        meta['csrf_context'] = {
            'csrf_token': get_token(request)
        }
        kwargs['meta'] = meta
        super().__init__(*args, **kwargs)

# Usage in Django view
def form_view(request):
    form = DjangoForm(request, data=request.POST or None)
    if form.validate():
        # Process form
        pass
    return render(request, 'form.html', {'form': form})

CSRF Configuration Best Practices

import os
from wtforms import Form

class ProductionForm(Form):
    class Meta:
        # Enable CSRF protection
        csrf = True
        
        # Use environment variable for secret
        csrf_secret = os.environ.get('CSRF_SECRET_KEY', '').encode()
        
        # Custom field name for security through obscurity
        csrf_field_name = 'authenticity_token'
        
        # Session context will be set per request
        csrf_context = None
        
    def __init__(self, *args, **kwargs):
        # Validate configuration
        if not self.Meta.csrf_secret:
            raise ValueError("CSRF_SECRET_KEY environment variable required")
        
        super().__init__(*args, **kwargs)

# Environment setup
# export CSRF_SECRET_KEY="your-long-random-secret-key"

Install with Tessl CLI

npx tessl i tessl/pypi-wtforms

docs

csrf.md

fields.md

forms.md

i18n.md

index.md

validation.md

widgets.md

tile.json