Form validation and rendering for Python web development.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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."""
passAbstract 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
"""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)<!-- 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>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 formfrom 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
passclass 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)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')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# 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'})
});
});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)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})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