coldbox-security-csrf
Use this skill when implementing CSRF (Cross-Site Request Forgery) protection in ColdBox forms, using cbcsrf to generate and validate tokens, adding csrf() tokens to HTML forms, validating tokens in POST/PUT/DELETE handlers, configuring the cbcsrf module, or excluding API routes from CSRF verification.
CSRF Protection in ColdBox
Overview
CSRF (Cross-Site Request Forgery) attacks trick authenticated users into executing unintended actions. The cbcsrf module generates and validates unique per-session tokens for all state-changing requests.
Language Mode Reference
Examples use BoxLang (.bx) syntax by default. Adapt for your target language:
| Concept | BoxLang (.bx) | CFML (.cfc) |
|---|---|---|
| Class declaration | class [extends="..."] { | component [extends="..."] { |
| DI annotation | @inject above property name="svc"; | property name="svc" inject="svc"; |
| View templates | .bxm suffix | .cfm / .cfml suffix |
| Tag prefix | <bx:if>, <bx:output>, <bx:set> | <cfif>, <cfoutput>, <cfset> |
CFML Compat Mode: With BoxLang + CFML Compat module,
.bxand.cfcfiles coexist freely. BoxLang-native classes useclass {}(.bxfiles); CFML-compat classes usecomponent {}(.cfcfiles).
Installation
box install cbcsrf
Configuration
// config/ColdBox.cfc
moduleSettings = {
cbcsrf: {
enabled: true,
tokenKey: "_csrftoken",
// Rotate token for each request (more secure, may cause issues with multi-tab)
rotateTokens: false,
// Which HTTP methods require verification
verifyMethod: "all", // or "post", "delete", "put", "patch"
// Token expiration in minutes
tokenExpiration: 30,
// Exclude these event/route patterns from CSRF
exclude: [
"^api\\..*" // exclude all API routes
]
}
}
Adding CSRF Token to HTML Forms
<!-- views/users/create.cfm -->
<form action="#event.buildLink( 'users.store' )#" method="post">
<!-- Drop the CSRF token field — auto-generates the hidden input -->
#csrf()#
<div>
<label>Name: <input type="text" name="name" required /></label>
</div>
<div>
<label>Email: <input type="email" name="email" required /></label>
</div>
<button type="submit">Create User</button>
</form>
Manual Token Generation
// In handler — pass token to view
function create( event, rc, prc ) {
prc.csrfToken = generateCSRFToken()
event.setView( "users/create" )
}
<!-- View with manual token -->
<form method="post">
<input type="hidden" name="_csrftoken" value="#prc.csrfToken#" />
<!-- ...fields... -->
</form>
Validating CSRF in Handlers
/**
* handlers/Users.cfc
*/
class extends="coldbox.system.EventHandler" {
// POST /users
function store( event, rc, prc ) {
// cbcsrf automatically validates on POST actions
// If token is invalid it throws an exception
// Manual validation if needed:
if ( !verifyCSRFToken( rc._csrftoken ) ) {
flash.put( "error", "Invalid security token. Please try again." )
relocate( "users.create" )
}
userService.create( {
name: rc.name,
email: rc.email
} )
flash.put( "success", "User created!" )
relocate( "users.index" )
}
}
CFML (.cfc):
/**
* handlers/Users.cfc
*/
component extends="coldbox.system.EventHandler" {
// POST /users
function store( event, rc, prc ) {
// cbcsrf automatically validates on POST actions
// If token is invalid it throws an exception
// Manual validation if needed:
if ( !verifyCSRFToken( rc._csrftoken ) ) {
flash.put( "error", "Invalid security token. Please try again." )
relocate( "users.create" )
}
userService.create( {
name: rc.name,
email: rc.email
} )
flash.put( "success", "User created!" )
relocate( "users.index" )
}
}
CSRF with AJAX Requests
// Include CSRF token in AJAX requests via header
fetch('/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ name: 'John', email: '[email protected]' })
})
<!-- Add CSRF token as meta tag in layout -->
<head>
<meta name="csrf-token" content="#generateCSRFToken()#" />
</head>
Excluding API Routes
moduleSettings = {
cbcsrf: {
// Exclude all API routes — APIs use JWT/API key auth instead
exclude: [
"^api\\..*",
"^webhook\\..*"
]
}
}
Interceptor-Based Validation
/**
* interceptors/CSRFInterceptor.cfc
* Global CSRF validation for all POST requests
*/
class extends="coldbox.system.Interceptor" {
function preProcess( event, interceptData ) {
// Only check state-changing methods
if ( !listContains( "POST,PUT,PATCH,DELETE", event.getHTTPMethod() ) ) {
return
}
// Skip API routes (use JWT instead)
if ( event.getCurrentEvent() startsWith "api." ) {
return
}
// Validate token
var token = event.getValue( "_csrftoken", "" )
if ( !verifyCSRFToken( token ) ) {
flash.put( "error", "Your session may have expired. Please try again." )
relocate( event.getCurrentRoutedURL() )
}
}
}
CFML (.cfc):
/**
* interceptors/CSRFInterceptor.cfc
* Global CSRF validation for all POST requests
*/
component extends="coldbox.system.Interceptor" {
function preProcess( event, interceptData ) {
// Only check state-changing methods
if ( !listContains( "POST,PUT,PATCH,DELETE", event.getHTTPMethod() ) ) {
return
}
// Skip API routes (use JWT instead)
if ( event.getCurrentEvent() startsWith "api." ) {
return
}
// Validate token
var token = event.getValue( "_csrftoken", "" )
if ( !verifyCSRFToken( token ) ) {
flash.put( "error", "Your session may have expired. Please try again." )
relocate( event.getCurrentRoutedURL() )
}
}
}
CSRF Token Helpers Reference
| Function | Description |
|---|---|
csrf() | Generate <input type="hidden"> field with token |
generateCSRFToken() | Return raw token string |
verifyCSRFToken( token ) | Validate a token string, returns boolean |
Security Notes
- CSRF protection complements (doesn't replace) authentication
- API routes relying on JWT/API keys don't need CSRF tokens — exclude them
- Tokens are tied to the user's session
rotateTokens: trueis more secure but may break browser back-button behavior- Always use HTTPS so tokens can't be intercepted