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();
    }
}

Parameters

Parameter
Type
Description

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()

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;
        }
    }
}

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();
    }
}

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 );
    }
}

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() {}

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:

  1. Calculates a cryptographic checksum of the component snapshot using HMAC-SHA256

  2. Includes the checksum with the data payload

  3. Validates the checksum on subsequent requests

  4. Throws CBWIRECorruptPayloadException if 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"
    }
};

If you don't provide a custom secret, CBWIRE generates one automatically based on the module's installation path. For production deployments across multiple servers, set a consistent secret key.

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:

  1. CBWIRE generates a unique token when components mount

  2. The token is included with every client-server interaction

  3. Server validates the token before processing requests

  4. 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
    }
}

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 csrfEnabled setting to enable/disable

  • Flexible csrfStorage setting for storage backend

  • No dependency on external CSRF modules

Backward Compatibility:

  • Public API remains unchanged

  • Existing code continues working without modifications

  • Optional migration to session storage

Security annotations are only active when cbSecurity is installed and configured. Without cbSecurity, use the onSecure() lifecycle method for custom security logic.

Last updated

Was this helpful?