Security
CBWIRE provides multiple ways to secure your wire components, from simple authentication checks to integration with the cbSecurity module for role and permission-based authorization.
onSecure() Lifecycle Method
The onSecure() lifecycle method runs before all other lifecycle methods, allowing you to enforce security rules at the earliest point in the request lifecycle. Return false to halt processing and render an empty div, or return nothing to continue processing normally.
// wires/AdminDashboard.bx
class extends="cbwire.models.Component" {
property name="authService" inject="AuthService";
data = {
"stats": {}
};
function onSecure( event, rc, prc, isInitial, params ) {
// Check authentication
if ( !authService.isLoggedIn() ) {
return false;
}
// Check authorization
if ( !authService.hasRole( "admin" ) ) {
return false;
}
}
function onMount() {
data.stats = getAdminStats();
}
}// wires/AdminDashboard.cfc
component extends="cbwire.models.Component" {
property name="authService" inject="AuthService";
data = {
"stats" = {}
};
function onSecure( event, rc, prc, isInitial, params ) {
// Check authentication
if ( !authService.isLoggedIn() ) {
return false;
}
// Check authorization
if ( !authService.hasRole( "admin" ) ) {
return false;
}
}
function onMount() {
data.stats = getAdminStats();
}
}Parameters
event
object
The ColdBox request context
rc
struct
Request collection
prc
struct
Private request collection
isInitial
boolean
True for initial mount, false for subsequent AJAX requests
params
struct
Parameters passed to the component via wire()
onSecure() fires on every request—both initial rendering and all subsequent AJAX requests. This ensures security checks run continuously throughout the component's lifecycle.
Custom Failure Messages
By default, returning false from onSecure() renders an empty div. Customize this message per component or globally:
// wires/SecureContent.bx
class extends="cbwire.models.Component" {
property name="authService" inject="AuthService";
// Custom message when security check fails
secureMountFailMessage = "<div class='alert alert-warning'>Please log in to view this content.</div>";
data = {
"content": ""
};
function onSecure( event, rc, prc, isInitial, params ) {
if ( !authService.isLoggedIn() ) {
return false;
}
}
}// wires/SecureContent.cfc
component extends="cbwire.models.Component" {
property name="authService" inject="AuthService";
// Custom message when security check fails
secureMountFailMessage = "<div class='alert alert-warning'>Please log in to view this content.</div>";
data = {
"content" = ""
};
function onSecure( event, rc, prc, isInitial, params ) {
if ( !authService.isLoggedIn() ) {
return false;
}
}
}Set a global default in your ColdBox configuration:
// config/ColdBox.cfc
moduleSettings = {
cbwire = {
secureMountFailMessage = "<div class='alert alert-danger'>Access Denied</div>"
}
};Component-level settings take precedence over module-level configuration.
cbSecurity Integration
When the cbSecurity module is installed and active, CBWIRE automatically integrates with it, enabling security annotations on components and methods.
Component-Level Security
Secure an entire component with the secured annotation:
// wires/AdminPanel.bx
@secured
class extends="cbwire.models.Component" {
data = {
"users": []
};
function onMount() {
data.users = userService.getAll();
}
}// wires/AdminPanel.cfc
component extends="cbwire.models.Component" secured {
data = {
"users" = []
};
function onMount() {
data.users = userService.getAll();
}
}Method-Level Security
Secure specific actions with annotations:
// wires/UserManagement.bx
class extends="cbwire.models.Component" {
data = {
"users": []
};
function onMount() {
data.users = userService.getAll();
}
// Only users with "admin" permission can execute
@secured("admin")
function deleteUser( userId ) {
userService.delete( userId );
data.users = userService.getAll();
}
// Multiple permissions (user needs any one)
@secured("admin,moderator")
function updateUser( userId ) {
userService.update( userId, data.users );
}
}// wires/UserManagement.cfc
component extends="cbwire.models.Component" {
data = {
"users" = []
};
function onMount() {
data.users = userService.getAll();
}
// Only users with "admin" permission can execute
function deleteUser( userId ) secured="admin" {
userService.delete( userId );
data.users = userService.getAll();
}
// Multiple permissions (user needs any one)
function updateUser( userId ) secured="admin,moderator" {
userService.update( userId, data.users );
}
}Annotation Formats
cbSecurity annotations support multiple formats:
// Boolean - uses cbSecurity default rules
@secured
function someAction() {}
@secured(true)
function someAction() {}
// Single permission
@secured("admin")
function someAction() {}
// Multiple permissions (comma-separated)
@secured("admin,moderator,editor")
function someAction() {}// Boolean - uses cbSecurity default rules
function someAction() secured {}
function someAction() secured=true {}
// Single permission
function someAction() secured="admin" {}
// Multiple permissions (comma-separated)
function someAction() secured="admin,moderator,editor" {}Security Interceptor
CBWIRE fires the onCBWIRESecureFail interception point when security checks fail, allowing custom handling like logging or redirects.
// interceptors/SecurityLogger.cfc
component {
function onCBWIRESecureFail( event, data, rc, prc ) {
var componentName = data.componentName ?: "Unknown";
var methodName = data.methodName ?: "N/A";
var user = auth.user();
writeLog(
type = "warning",
file = "security",
text = "Security failure: User ##user.getId()## attempted to access #componentName#.#methodName#"
);
// Optional: redirect to login
if ( !auth.check() ) {
relocate( "login" );
}
}
}Register the interceptor in your ColdBox configuration:
// config/ColdBox.cfc
interceptors = [
{ class="interceptors.SecurityLogger" }
];Checksum Validation
CBWIRE automatically validates component data integrity using HMAC-SHA256 checksums to prevent tampering with data between client and server. Every component snapshot includes a checksum that validates the data hasn't been modified.
How It Works
When data travels between client and server, CBWIRE:
Calculates a cryptographic checksum of the component snapshot using HMAC-SHA256
Includes the checksum with the data payload
Validates the checksum on subsequent requests
Throws
CBWIRECorruptPayloadExceptionif validation fails
The checksum uses a secret key—either a custom secret you provide or a hash of the module's root path.
Configuration
Control checksum validation in your ColdBox configuration:
// config/ColdBox.cfc
moduleSettings = {
cbwire = {
// Disable checksum validation (not recommended for production)
checksumValidation = false,
// Optional: provide a custom secret key
secret = "your-secret-key-here"
}
};Disabling checksum validation removes protection against client-side data tampering. Only disable this in controlled development environments where you trust all clients.
Error Handling
Checksum validation failures throw CBWIRECorruptPayloadException in these scenarios:
Invalid JSON: Payload is not properly formatted JSON
Missing Checksum: No checksum found in the payload
Checksum Mismatch: Calculated checksum doesn't match the provided checksum
These errors typically indicate:
Client-side data tampering attempts
Network corruption during transmission
Version mismatches between client and server code
Different secret keys across application servers
CSRF Protection
CBWIRE includes built-in Cross-Site Request Forgery (CSRF) protection to prevent malicious websites from submitting unauthorized requests to your application. CSRF tokens are automatically generated and validated for all component interactions.
How It Works
CSRF protection operates transparently:
CBWIRE generates a unique token when components mount
The token is included with every client-server interaction
Server validates the token before processing requests
Invalid or missing tokens reject the request
No changes to your component code are required—CSRF protection works automatically when enabled.
Configuration
CSRF protection is enabled by default in CBWIRE 5.x. Configure it in your ColdBox settings:
// config/ColdBox.cfc
moduleSettings = {
cbwire = {
// Enable/disable CSRF protection (enabled by default)
csrfEnabled = true,
// Storage implementation (defaults to SessionCSRFStorage)
csrfStorage = "SessionCSRFStorage@cbwire"
}
};Storage Implementations
CBWIRE provides two built-in storage implementations for CSRF tokens:
SessionCSRFStorage (Default)
Session-based storage is the OWASP-recommended approach and the default in CBWIRE 5.x:
moduleSettings = {
cbwire = {
csrfStorage = "SessionCSRFStorage@cbwire"
}
};Benefits:
Eliminates "Page Expired" errors common with cache-based storage
Follows OWASP security recommendations
Simpler configuration for single-server deployments
Requirements:
Application sessions must be enabled
CacheCSRFStorage
Cache-based storage is designed for distributed and clustered deployments:
moduleSettings = {
cbwire = {
csrfStorage = "CacheCSRFStorage@cbwire"
}
};Benefits:
Works across multiple application servers
No session requirement
Suitable for stateless architectures
Considerations:
Requires distributed cache configuration
Uses cookie/URL fallback when cache is unavailable
Custom Storage
Implement custom CSRF storage (Redis, DynamoDB, etc.) by creating a component that implements the ICSRFStorage interface:
// models/RedisCSRFStorage.bx
class implements="cbwire.models.ICSRFStorage" {
property name="redisClient" inject="RedisClient";
function set( key, value ) {
redisClient.set( key, value );
}
function get( key ) {
return redisClient.get( key );
}
function exists( key ) {
return redisClient.exists( key );
}
function delete( key ) {
redisClient.del( key );
}
function clear() {
// Implement based on your requirements
}
}// models/RedisCSRFStorage.cfc
component implements="cbwire.models.ICSRFStorage" {
property name="redisClient" inject="RedisClient";
function set( key, value ) {
redisClient.set( key, value );
}
function get( key ) {
return redisClient.get( key );
}
function exists( key ) {
return redisClient.exists( key );
}
function delete( key ) {
redisClient.del( key );
}
function clear() {
// Implement based on your requirements
}
}Configure your custom implementation:
moduleSettings = {
cbwire = {
csrfStorage = "RedisCSRFStorage@myapp"
}
};Changes in 5.x
CBWIRE 5.x refactored CSRF protection with significant improvements:
Session Storage by Default:
Changed from cache-based to session-based storage
Eliminates "Page Expired" errors
Aligns with OWASP recommendations
Extensible Architecture:
Interface-based design allows custom implementations
Easy integration with any storage backend
Two built-in implementations for common scenarios
Simplified Configuration:
Single
csrfEnabledsetting to enable/disableFlexible
csrfStoragesetting for storage backendNo dependency on external CSRF modules
Backward Compatibility:
Public API remains unchanged
Existing code continues working without modifications
Optional migration to session storage
Security checks run on every request. For performance-critical components, consider caching authorization results or using efficient permission lookups.
Last updated
Was this helpful?