or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

csrf.mdfields.mdforms.mdi18n.mdindex.mdvalidation.mdwidgets.md

csrf.mddocs/

0

# CSRF Protection

1

2

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.

3

4

## Capabilities

5

6

### CSRF Field Types

7

8

Special field types for handling CSRF tokens.

9

10

```python { .api }

11

class CSRFTokenField(HiddenField):

12

"""

13

Hidden field for CSRF tokens.

14

Automatically renders current token and never populates object data.

15

Extends HiddenField with CSRF-specific behavior.

16

"""

17

def __init__(self, label=None, validators=None, **kwargs): ...

18

19

def populate_obj(self, obj, name):

20

"""Override to prevent populating object attributes."""

21

pass

22

```

23

24

### Core CSRF Classes

25

26

Abstract base and concrete implementations for CSRF protection.

27

28

```python { .api }

29

class CSRF:

30

"""

31

Abstract base class for CSRF protection implementations.

32

Defines the interface for CSRF token generation and validation.

33

"""

34

def setup_form(self, form) -> list:

35

"""

36

Add CSRF fields to form.

37

38

Parameters:

39

- form: Form instance to protect

40

41

Returns:

42

list: List of (field_name, unbound_field) tuples to add to form

43

"""

44

45

def generate_csrf_token(self, csrf_token_field) -> str:

46

"""

47

Generate CSRF token (abstract method).

48

49

Parameters:

50

- csrf_token_field: CSRFTokenField instance

51

52

Returns:

53

str: Generated CSRF token

54

"""

55

raise NotImplementedError()

56

57

def validate_csrf_token(self, form, field):

58

"""

59

Validate CSRF token (abstract method).

60

61

Parameters:

62

- form: Form instance

63

- field: CSRFTokenField instance

64

65

Raises:

66

ValidationError: If token is invalid

67

"""

68

raise NotImplementedError()

69

70

class SessionCSRF(CSRF):

71

"""

72

Session-based CSRF implementation using HMAC-SHA1.

73

Stores token data in session and validates against generated tokens.

74

75

Parameters:

76

- secret_key: Secret key for HMAC generation (bytes)

77

- timeout: Token timeout in seconds (None for no timeout)

78

"""

79

def __init__(self, secret_key, timeout=None): ...

80

81

def generate_csrf_token(self, csrf_token_field) -> str:

82

"""

83

Generate HMAC-SHA1 based CSRF token.

84

85

Returns:

86

str: Base64 encoded token with timestamp and hash

87

"""

88

89

def validate_csrf_token(self, form, field):

90

"""

91

Validate CSRF token against session data and timestamp.

92

93

Raises:

94

ValidationError: If token is missing, expired, or invalid

95

"""

96

```

97

98

## CSRF Usage Examples

99

100

### Basic CSRF Protection

101

102

```python

103

from wtforms import Form, StringField, validators

104

from wtforms.csrf.session import SessionCSRF

105

from wtforms.meta import DefaultMeta

106

107

class SecureForm(Form):

108

class Meta(DefaultMeta):

109

csrf = True

110

csrf_secret = b'your-secret-key-here' # Must be bytes

111

csrf_context = session # Session-like object (Flask session, etc.)

112

csrf_field_name = 'csrf_token' # Default field name

113

114

message = StringField('Message', [validators.DataRequired()])

115

116

# Usage with Flask

117

@app.route('/contact', methods=['GET', 'POST'])

118

def contact():

119

form = SecureForm(formdata=request.form, meta={'csrf_context': session})

120

121

if form.validate():

122

# Form passed CSRF validation

123

process_message(form.message.data)

124

return redirect('/success')

125

126

return render_template('contact.html', form=form)

127

```

128

129

### Template Usage

130

131

```html

132

<!-- In your HTML template -->

133

<form method="POST">

134

{{ form.csrf_token }} <!-- Hidden CSRF field -->

135

{{ form.message.label }} {{ form.message() }}

136

<input type="submit" value="Submit">

137

</form>

138

139

<!-- Or explicitly render CSRF token -->

140

<form method="POST">

141

<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>

142

<!-- Other form fields -->

143

</form>

144

```

145

146

### Custom CSRF Configuration

147

148

```python

149

from wtforms.csrf.session import SessionCSRF

150

151

class CustomCSRFForm(Form):

152

class Meta:

153

csrf = True

154

csrf_secret = b'super-secret-key'

155

csrf_field_name = 'authenticity_token' # Custom field name

156

csrf_context = None # Will be set per request

157

csrf_class = SessionCSRF # Explicit CSRF class

158

159

name = StringField('Name')

160

email = StringField('Email')

161

162

# Per-request context setting

163

def create_form(request):

164

form = CustomCSRFForm(

165

formdata=request.form,

166

meta={'csrf_context': request.session}

167

)

168

return form

169

```

170

171

### CSRF with Timeout

172

173

```python

174

from wtforms.csrf.session import SessionCSRF

175

176

class TimedCSRFForm(Form):

177

class Meta:

178

csrf = True

179

csrf_secret = b'your-secret-key'

180

csrf_context = session

181

182

# Custom CSRF with 30 minute timeout

183

@property

184

def csrf_class(self):

185

return SessionCSRF(secret_key=self.Meta.csrf_secret, timeout=1800)

186

187

data = StringField('Data')

188

189

# Tokens will expire after 30 minutes

190

form = TimedCSRFForm(formdata=request.form)

191

if form.validate():

192

# Process valid form with non-expired token

193

pass

194

```

195

196

### Multiple Forms with CSRF

197

198

```python

199

class LoginForm(Form):

200

class Meta:

201

csrf = True

202

csrf_secret = b'login-secret'

203

csrf_field_name = 'login_token'

204

csrf_context = session

205

206

username = StringField('Username')

207

password = StringField('Password')

208

209

class RegistrationForm(Form):

210

class Meta:

211

csrf = True

212

csrf_secret = b'register-secret' # Different secret

213

csrf_field_name = 'register_token' # Different field name

214

csrf_context = session

215

216

username = StringField('Username')

217

email = StringField('Email')

218

password = StringField('Password')

219

220

# Both forms can coexist with different CSRF tokens

221

@app.route('/auth', methods=['GET', 'POST'])

222

def auth():

223

login_form = LoginForm(formdata=request.form, prefix='login')

224

register_form = RegistrationForm(formdata=request.form, prefix='register')

225

226

if 'login_submit' in request.form and login_form.validate():

227

# Process login

228

pass

229

elif 'register_submit' in request.form and register_form.validate():

230

# Process registration

231

pass

232

233

return render_template('auth.html',

234

login_form=login_form,

235

register_form=register_form)

236

```

237

238

### Custom CSRF Implementation

239

240

```python

241

from wtforms.csrf.core import CSRF, CSRFTokenField

242

from wtforms.validators import ValidationError

243

import hmac

244

import hashlib

245

import time

246

import base64

247

248

class DatabaseCSRF(CSRF):

249

"""Custom CSRF implementation using database storage."""

250

251

def __init__(self, secret_key, db_session, timeout=3600):

252

self.secret_key = secret_key

253

self.db_session = db_session

254

self.timeout = timeout

255

256

def generate_csrf_token(self, csrf_token_field):

257

# Generate unique token

258

timestamp = str(int(time.time()))

259

user_id = str(getattr(csrf_token_field.form, 'current_user_id', 0))

260

261

# Create token data

262

token_data = f"{timestamp}:{user_id}"

263

signature = hmac.new(

264

self.secret_key,

265

token_data.encode('utf-8'),

266

hashlib.sha256

267

).hexdigest()

268

269

token = base64.b64encode(f"{token_data}:{signature}".encode()).decode()

270

271

# Store in database

272

csrf_record = CSRFToken(token=token, created_at=time.time())

273

self.db_session.add(csrf_record)

274

self.db_session.commit()

275

276

return token

277

278

def validate_csrf_token(self, form, field):

279

if not field.data:

280

raise ValidationError('CSRF token missing')

281

282

try:

283

# Decode token

284

decoded = base64.b64decode(field.data).decode()

285

timestamp, user_id, signature = decoded.split(':')

286

287

# Verify signature

288

expected_data = f"{timestamp}:{user_id}"

289

expected_signature = hmac.new(

290

self.secret_key,

291

expected_data.encode('utf-8'),

292

hashlib.sha256

293

).hexdigest()

294

295

if not hmac.compare_digest(signature, expected_signature):

296

raise ValidationError('Invalid CSRF token')

297

298

# Check timeout

299

if time.time() - int(timestamp) > self.timeout:

300

raise ValidationError('CSRF token expired')

301

302

# Verify token exists in database

303

csrf_record = self.db_session.query(CSRFToken).filter_by(

304

token=field.data

305

).first()

306

307

if not csrf_record:

308

raise ValidationError('CSRF token not found')

309

310

# Remove used token (one-time use)

311

self.db_session.delete(csrf_record)

312

self.db_session.commit()

313

314

except (ValueError, TypeError):

315

raise ValidationError('Malformed CSRF token')

316

317

class DatabaseProtectedForm(Form):

318

class Meta:

319

csrf = True

320

csrf_class = DatabaseCSRF

321

csrf_secret = b'database-csrf-secret'

322

323

def __init__(self, db_session, *args, **kwargs):

324

# Inject database session into CSRF

325

meta = kwargs.get('meta', {})

326

meta['csrf_class'] = DatabaseCSRF(

327

secret_key=self.Meta.csrf_secret,

328

db_session=db_session

329

)

330

kwargs['meta'] = meta

331

super().__init__(*args, **kwargs)

332

333

data = StringField('Data')

334

```

335

336

### CSRF Error Handling

337

338

```python

339

from wtforms.validators import ValidationError

340

341

class CSRFProtectedForm(Form):

342

class Meta:

343

csrf = True

344

csrf_secret = b'secret-key'

345

csrf_context = session

346

347

message = StringField('Message')

348

349

@app.route('/submit', methods=['POST'])

350

def submit():

351

form = CSRFProtectedForm(formdata=request.form)

352

353

try:

354

if form.validate():

355

# Process valid form

356

return jsonify({'status': 'success'})

357

else:

358

# Handle validation errors

359

errors = {}

360

for field_name, field_errors in form.errors.items():

361

if field_name == 'csrf_token':

362

# CSRF-specific error handling

363

return jsonify({

364

'status': 'error',

365

'message': 'Security token invalid. Please refresh and try again.'

366

}), 403

367

errors[field_name] = field_errors

368

369

return jsonify({'status': 'error', 'errors': errors}), 400

370

371

except ValidationError as e:

372

# Handle CSRF validation exceptions

373

return jsonify({

374

'status': 'error',

375

'message': 'Security validation failed'

376

}), 403

377

```

378

379

### CSRF with AJAX

380

381

```python

382

# Server-side: Provide CSRF token for AJAX requests

383

@app.route('/api/csrf-token')

384

def csrf_token():

385

form = CSRFProtectedForm() # Create form to get token

386

return jsonify({'csrf_token': form.csrf_token.data})

387

388

# Client-side JavaScript

389

fetch('/api/csrf-token')

390

.then(response => response.json())

391

.then(data => {

392

// Include CSRF token in subsequent requests

393

fetch('/api/submit', {

394

method: 'POST',

395

headers: {

396

'Content-Type': 'application/json',

397

'X-CSRF-Token': data.csrf_token

398

},

399

body: JSON.stringify({message: 'Hello World'})

400

});

401

});

402

```

403

404

### Framework Integration Examples

405

406

#### Flask Integration

407

408

```python

409

from flask import Flask, session, request

410

from wtforms.csrf.session import SessionCSRF

411

412

app = Flask(__name__)

413

app.secret_key = 'your-flask-secret'

414

415

class FlaskForm(Form):

416

class Meta:

417

csrf = True

418

csrf_secret = app.secret_key.encode()

419

csrf_context = session

420

421

def __init__(self, *args, **kwargs):

422

super().__init__(*args, **kwargs)

423

424

# Usage

425

@app.route('/form', methods=['GET', 'POST'])

426

def form_view():

427

form = FlaskForm(formdata=request.form)

428

if form.validate():

429

# Process form

430

pass

431

return render_template('form.html', form=form)

432

```

433

434

#### Django Integration

435

436

```python

437

from django.middleware.csrf import get_token

438

439

class DjangoForm(Form):

440

class Meta:

441

csrf = True

442

csrf_secret = settings.SECRET_KEY.encode()

443

444

def __init__(self, request, *args, **kwargs):

445

# Use Django's CSRF token

446

meta = kwargs.get('meta', {})

447

meta['csrf_context'] = {

448

'csrf_token': get_token(request)

449

}

450

kwargs['meta'] = meta

451

super().__init__(*args, **kwargs)

452

453

# Usage in Django view

454

def form_view(request):

455

form = DjangoForm(request, data=request.POST or None)

456

if form.validate():

457

# Process form

458

pass

459

return render(request, 'form.html', {'form': form})

460

```

461

462

### CSRF Configuration Best Practices

463

464

```python

465

import os

466

from wtforms import Form

467

468

class ProductionForm(Form):

469

class Meta:

470

# Enable CSRF protection

471

csrf = True

472

473

# Use environment variable for secret

474

csrf_secret = os.environ.get('CSRF_SECRET_KEY', '').encode()

475

476

# Custom field name for security through obscurity

477

csrf_field_name = 'authenticity_token'

478

479

# Session context will be set per request

480

csrf_context = None

481

482

def __init__(self, *args, **kwargs):

483

# Validate configuration

484

if not self.Meta.csrf_secret:

485

raise ValueError("CSRF_SECRET_KEY environment variable required")

486

487

super().__init__(*args, **kwargs)

488

489

# Environment setup

490

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

491

```