Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
What's new with CBSecurity 3.4.3
CBSecurity 3.4.3 is a maintenance release that addresses ColdBox 7 compatibility requirements.
The primary change in this release addresses a breaking change in ColdBox 7:
Fixed: Renamed renderView() to view() to be ColdBox 7 compliant
This change ensures CBSecurity works properly with ColdBox 7's updated view rendering methods
Maintains backward compatibility with earlier ColdBox versions
ColdBox Framework: 6+ (ColdBox 7 compliant)
CFML Engines: Adobe ColdFusion 2018+, Lucee 5+
CommandBox: 5.0+
This release maintains full backward compatibility with existing CBSecurity 3.x installations while ensuring forward compatibility with ColdBox 7.
No migration steps are required for this release. Simply update your CBSecurity module dependency:
What's new with CBSecurity 3.5.0
CBSecurity 3.5.0 is a significant modernization release that brings enhanced platform support, improved development workflows, and comprehensive AI assistance capabilities.
CBSecurity 3.5.0 has been fully certified for BoxLang, the modern dynamic JVM language. This includes:
Complete compatibility testing with BoxLang runtime
A brief history in time of our major releases
In this section you will find the release notes for each version we release under this major version. If you are looking for the release notes of previous major versions use the version switcher at the top left of this documentation book. Here is a breakdown of our major version releases.
Version 3 is a major rewrite of this module. It drops Adobe 2016 support and enhances the way the firewall is configured. It also add major capabilities for security headers, csrf settings and much more.
It also introduces the ability for the firewall to do 401 response blocks as actions for security rules. The CBSecurity visualizer is also a major addition that allows a developer or manager to visualize the performance of the firewall and visualize all the configurations necessary for operation.
Version 2 is a major release of our security module. We completely refactored the engine to make it more modern and to adhere to our new coding standards. We then proceeded to enhance it to tap into our HMVC approach and allow rules to be contributed from modules themselves. We also added annotation driven security to complete the ability to secure not only incoming requests by rules but also by easy annotations.
We have made great strides in this release to make it a one-stop-shop for security concerns within ColdBox applications.
Our first release as a module decoupled from the ColdBox 2 days!
Validated functionality across all CBSecurity features
Updated examples and documentation for BoxLang syntax
Full test harness coverage for BoxLang environments
This release adds official support and certification for ColdBox 8, ensuring CBSecurity works seamlessly with the latest ColdBox framework features and improvements.
The project has migrated to modern GitHub Actions workflows, providing:
Improved CI/CD pipeline reliability
Better cross-platform testing coverage
Automated testing across multiple CFML engines
Enhanced security scanning and dependency management
The test harness has been significantly upgraded to provide:
Better local development experience
Enhanced integration testing capabilities
Improved TestBox runner configuration
Streamlined server startup and configuration
CBSecurity 3.5.0 introduces comprehensive AI assistance through:
.github/copilot-instructions.md - Detailed guidance for AI agents covering:
Module architecture and component relationships
Security validator patterns and implementation
Interceptor flow and event handling
Development workflows and best practices
Test harness setup and TestBox runner details
This enhancement enables AI tools like GitHub Copilot to provide more accurate and contextual assistance when working with CBSecurity.
Documented test-harness structure and usage patterns
Enhanced TestBox runner details for local integration testing
Improved developer workflow documentation
Better guidance for module extension and customization
The release includes improved documentation and tooling for:
Setting up local development environments
Running integration tests via test-harness/tests/runner.cfm
Using box.json scripts for common development tasks
Server configuration for different CFML engines
ColdBox Framework: 6+ (ColdBox 8 certified)
CFML Engines: BoxLang 1+ (Preferred), Lucee 5+, Adobe 2023+
CommandBox: 5.0+
This release maintains full backward compatibility with existing CBSecurity 3.x installations. No breaking changes have been introduced.
No migration steps are required for this release. Simply update your CBSecurity module dependency:
Fixed: Updated security logs columns to work with Oracle databases using clob data type
This enhancement ensures CBSecurity's logging functionality works seamlessly with Oracle database environments
Improves enterprise database compatibility for security audit trails
Fixed: cbsecurity_logs table name is now properly referenced instead of being hard-coded
This change ensures the module setting for the logs table name is properly respected
Provides better flexibility for custom table naming conventions
Fixed: Updated markdown rules to eliminate duplicate headers
Improved documentation consistency and readability
Enhanced GitBook compatibility and navigation structure
ColdBox Framework: 6+
CFML Engines: Adobe ColdFusion 2018+, Lucee 5+
CommandBox: 5.0+
Database: Any supported database (Oracle compatibility enhanced)
This release maintains full backward compatibility with existing CBSecurity 3.x installations while improving database compatibility across different database engines.
No migration steps are required for this release. Simply update your CBSecurity module dependency:
If you're using Oracle and experiencing issues with security logs, this update will resolve column type compatibility issues. No manual database changes are required.
# Update to latest version
box update cbsecurity
# Or install specific version
box install [email protected]# Update to latest version
box update cbsecurity
# Or install specific version
box install [email protected]# Update to latest version
box update cbsecurity
# Or install specific version
box install [email protected]Enterprise-grade security for ColdBox applications with authentication, authorization, JWT, CSRF protection, and comprehensive security headers.
CBSecurity is a comprehensive security framework for ColdBox applications, providing enterprise-grade authentication, authorization, and protection mechanisms. It combines multiple security modules into a cohesive, easy-to-use security platform that helps developers build secure applications with minimal effort.
CBSecurity provides a multi-layered security approach with the following key capabilities:
Security Firewall - Rule-based request protection using security rules engine and handler annotations
Authentication Manager (cbauth) - Pluggable authentication system compatible with any authentication provider
Basic Authentication - Built-in HTTP Basic Auth support with credential storage and browser challenge handling
JWT Services (jwtcfml) - Complete JSON Web Token implementation with generation, decoding, and validation
Access & Refresh Tokens - Native support for JWT-based authentication flows
Token Storage - Flexible token storage with multiple backend options
CSRF Protection (cbcsrf) - Cross-Site Request Forgery protection for form submissions
Security Headers - Industry-standard HTTP response headers (CSP, HSTS, X-Frame-Options, XSS Protection)
Password Generator - Cryptographically secure random password generation
Security Visualizer - Graphical interface for monitoring firewall activity and managing security configurations
Rule Engine - Flexible security rules supporting XML, JSON, database, and model-based configurations
Module Integration - Allows modules to contribute their own security rules and validation logic
CBSecurity is built on a modular architecture that integrates several specialized security modules:
The framework leverages cbstorages for flexible storage backends and seamlessly integrates with the ColdBox ecosystem to provide comprehensive security coverage across your entire application.
Multiple Storage Options - Define rules in XML, JSON, databases, or ColdBox models
Regular Expression Support - Use regex patterns or simple string matching for rule definitions
Modular Rules - Modules can contribute their own security rules with custom validation logic
Dynamic Rule Loading - Load and unload security rules at runtime from contributing modules
Annotation-Driven Security - Secure handlers and actions using ColdBox annotations
Cascading Security - Hierarchical security rules from global to handler to action level
Functional API - Injectable security service for authorization checks in any application layer
Custom Validators - Each module can define its own security validator implementation
Multiple Authentication Providers - Works with cbauth, ColdFusion native authentication, or custom providers
Provider Agnostic - Implements standard interfaces allowing any authentication system integration
Basic Authentication - Built-in HTTP Basic Auth with credential storage
Granular Control - Distinguish between authentication failures and authorization denials
Customizable Actions - Configure different responses for invalid authentication vs. authorization
Event-Driven - Hook into security events for custom logging, monitoring, or response handling
CBSecurity is open-source software licensed under the .
Documentation -
Source Code -
Issue Tracker -
Community Forum -
The ColdBox community is active and ready to help:
Community Forum - Ask questions and share knowledge with other developers
GitHub Issues - Report bugs and request features
Professional Support - Enterprise support available through Ortus Solutions
CBSecurity is professionally developed and supported by , a leader in CFML consulting and development.
Ortus Solutions offers comprehensive professional services for CBSecurity and the ColdBox Platform:
🛠️ Custom Development - Tailored security solutions for your specific requirements
👨🏫 Professional Support & Mentoring - Expert guidance from the creators of ColdBox
📚 Training - Official ColdBox and security training programs
🔍 Architecture & Code Reviews - Expert evaluation of your security implementation
Because of His grace, this project exists. If you don't like this, then don't read it; it's not for you.
"Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5
What's new with CBSecurity 3.4.1
CBSecurity 3.4.1 is a targeted maintenance release that addresses a specific database compatibility issue with Microsoft SQL Server.
Fixed: Added proper parenthesis on TOP statements for Microsoft SQL Server in the DBLogger
This fix resolves SQL syntax errors that were occurring when using CBSecurity's database logging features with MSSQL Server
Thanks to @irvirv for identifying and helping resolve this issue
ColdBox Framework: 6+
CFML Engines: Adobe ColdFusion 2018+, Lucee 5+
CommandBox: 5.0+
Database: Any supported database (MSSQL Server compatibility enhanced)
This release maintains full backward compatibility with existing CBSecurity 3.x installations while fixing a specific SQL syntax issue affecting Microsoft SQL Server users.
No migration steps are required for this release. Simply update your CBSecurity module dependency:
If you're using Microsoft SQL Server and experiencing SQL syntax errors in the DBLogger, this update will resolve those issues. No manual database changes are required.
Added transientCache=false to auth User to avoid any issues when doing security operations
Added population control for auth User for extra security
User auth was not serializing the id of the user in the mementifier config
In the previous releases the validator for JWT was JwtService@cbsecurity. This has now changed to JwtAuthValidator@cbsecurity. So make sure you update your configurations.
The CBAuthValidator has been renamed to just AuthValidator. This validator is now not cbauth focused but IAuthService focused. It also supports role and permission based authorization.
The entire settings structure has been redesigned to support many features in a a more concise and block approach. All top-level settings have been removed and added to specific sections. Please review the Configuration section in detail to see where the new settings belongs to.
2023-FEB-17
Added a new helper: createPassword() on the CBSecurity model to generate secure, random passwords with letters, symbols, and numbers.
cbcsrf Upgraded to version 3, which we missed in the previous release.
Secure access to views in ColdBox
You can also use our handy event.secureView() method in the request context to pivot between views according to user permissions.
event.secureView( permissions, successView, failView )This will allow you to set the successView if the user has the permissions or the failView if they don't.
The BasicAuth validator leverages HTTP Basic Authentication for authentication and role-permission-based authorization for you.
This validator will challenge users with browser-based basic authentication. It will use whatever authentication system and user service you have configured.
If you don't change the default settings, then CBSecurity will switch to using the BasicAuthUserService which allows you to store user credentials in your configuration file. You can check out our Basic Authentication section for further information.
cbsecurity = {
firewall : {
validator = "BasicAuthValidator@cbsecurity"
}
}Remember that a validator can exist globally and on a per ColdBox Module level.
The auth validator leverages authentication and role-permission-based authorization for you.
ColdBox security ships with this validator that knows how to talk to the authentication service and validate authentication and authorization via permissions and roles. All you need to do is use the WireBox ID of AuthValidator@cbsecurity in your validator setting:
cbsecurity = {
firewall : {
validator = "AuthValidator@cbsecurity"
}
}The configured authentication service must adhere to our IAuthService interface and the User object must adhere to the IAuthUser interface.
Remember that a validator can exist globally and on a per ColdBox Module level.
# Update to latest version
box update cbsecurity
# Or install specific version
box install [email protected]⚡ Performance Optimization - Server tuning and application optimization
🔐 Security Hardening - Comprehensive security audits and hardening services


Create fluent and secure code blocks
There are also times where you need to validate custom conditions and block access to certain areas. This way, you can implement your own custom security logic and leverage cbSecurity for blockage. You will accomplish this via the secureWhen() method. If the context evaluates to true then it will throw a NotAuthorized exception for you.
The context can be a closure/lambda/udf or a boolean evaluation:
The closure/udf will receive the currently authenticated user as the first argument.
You can also use the message argument to send your own message to the exception that's throw.
These methods assist a developer in getting insight into the authentication framework.
You can leverage the CBSecurity model to get insight into some aspects of the authentication process or do authentication via the configured authentication service if needed.
getAuthService() - Get access to the configured authentication service
getUserService() - Get access to the configured user service
authenticate( username, password ) - Authenticate a request
getUser() - Get the authenticated user of the current request
guest() - Verify if the users is NOT logged in, but a guest
CBSecurity comes bundled with a basic authentication User
CBSecurity ships with a basic User object that already implements the auth and JWT interfaces and gives you basic properties.
cbsecurity.models.auth.UserThis powerful component provides a comprehensive representation of users, with robust authorization and JSON Web Token (JWT) capabilities. With this basic user, you can effortlessly manage your application's user authentication and access control. It offers a user-friendly interface for handling user profiles, permissions, and JWT generation, ensuring a secure and seamless experience for developers and end-users.
Check out the ColdBox REST Starter Templates to see it in action.
coldbox create app name=restapp skeleton=restAbout our authors
Luis Majano is a Computer Engineer who has been developing and designing software systems since 2000. He was born in in the late 70s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelor of Science in Computer Engineering at .
He is the CEO of , a consulting firm specializing in web development, ColdFusion (CFML), Java development, and all open-source professional services under the ColdBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, CommandBox, LogBox, and anything “BOX” and contributes to many open-source ColdFusion/Java projects. You can read his blog at
Security rules in an XML file
You can place all your security rules inside of an XML file and then tell CBSecurity where they are:
Then your XML file can look like this:
Security rules from a database
CBSecurity also allows you to store your security rules in a database as long as all the columns match the keys of the rules as we saw in the
You will use the db as the source and fill out the available db properties:
The dsn property is the name of the datasource to use
A little more info about this book
The source code for this book is hosted on GitHub: . You can freely contribute to it and submit pull requests. The contents of this book are copyrighted by and cannot be altered or reproduced without the author's consent. All content is provided "As-Is" and can be freely distributed.
The majority of code examples in this book are done in cfscript.
The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL -
Configuring the CBSecurity Visualizer
The CBSecurity visualizer is a tool that will allow you to visualize all of your configuration settings, firewall logs, and much more. By default, the visualizer is disabled.
If you enable the visualizer, we highly suggest you secure it.
If enabled, you can visit the /cbsecurity entry point, and you will get the visualizer rendered.
Security rules from a model's method call
If you prefer to store your rules your way, CBSecurity can talk to any WireBox ID or model and get the rules from them by using the model source in the rule provider.
The model property is any WireBox ID or classpath
The method property is the name of the method to call to get an array of rules back
Configuring your authentication services
You will configure the authentication and user services in the authentication area of CBSecurity. CBSecurity ships with the module that can provide you with a robust authentication service, session/request storage, interceptions, and much more. However, you can use any security authentication service as long as it matches our interface: cbsecurity.interfaces.ISecurityValidator.
If you are using cbauth as your authenticationService (the default), you also need to
This object is used to provide you with human, fluent and explicit security authorizations, authentication insight, utility and contexts.
The cbSecurity model is a specialized service that will allow you to do explicit authorizations in any layer of your ColdBox application.
Sometimes, you will need authorization checks outside of the incoming request rules or the handler annotations. This can be from within interceptors, models, layouts, or views. For this, we have provided the cbSecurity model so you can do explicit authorization checks anywhere you like.
Verify permissions the fluent way!
If you just want to validate if a user has certain permissions or maybe no permissions at all or if a passed user is the same as the logged in user, then you can use the following boolean methods that only do verification.
Please note that you could potentially do these type of methods by leveraging the currently logged in user and it's hasPermission() method. However, these methods provide abstraction and can easily be mocked!
These are great to have a unified and abstracted way to verifying permissions or if the passed user is the same as the logged in user. Here are some examples:
View Layer
isLoggedIn() - Verify if the current request has authenticated
logout() - Logout the authenticated user
Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.
The information in this book is distributed “as is” without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity concerning loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software, and resources described in it.
We highly encourage contributions to this book and our open-source software. The source code for this book can be found in our GitHub repository, where you can submit pull requests.
10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - https://www.harvesting.org/. So please donate and purchase the printed version of this book; every book sold can help a child for almost two months.
Shalom Children’s Home is one of the ministries dear to our hearts in El Salvador. During the 12-year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.
Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education, and life skills training in a Christian environment. A child sponsorship program supports the home.
We have personally supported Shalom since; it is a place of blessing for many children in El Salvador who either have no families or have been abandoned. This is a good earth to seed and plant.
secureWhen( context, [errorMessage] )// Using as a closure/lambda
cbSecurity.secureWhen( ( user ) => !user.isConfirmed() )
cbSecurity.secureWhen( ( user ) => !oEntry.canPublish( user ) )
// Using a boolean evaluation
cbSecurity.secureWhen( cbSecurity.none( "AUTHOR_ADMIN" ) && !cbSecurity.sameUser( oAuthor ) )
cbSecurity.whenNone( "AUTHOR_ADMIN", ( user ) => relocate() );( user ) => {}
function( user );// CB Security
cbSecurity : {
firewall : {
rules : {
provider : {
"source" : "config/security.xml.cfm"
}
}
}
}<?xml version="1.0" encoding="ISO-8859-1"?>
<-- <
Declare as many rule elements as you want, order is important
Remember that the securelist can contain a list of regular
expressions if you want
ex: All events in the user handler
user\..*
ex: All events
.*
ex: All events that start with admin
^admin
If you are not using regular expressions, just write the text
that can be found in an event.
-->
<rules>
<rule>
<match>event</match>
<whitelist>user\.login,user\.logout,^main.*</whitelist>
<securelist>^user\..*, ^admin</securelist>
<roles>admin</roles>
<permissions>read,write</permissions>
<redirect>user.login</redirect>
</rule>
<rule>
<match>event</match>
<whitelist></whitelist>
<securelist>^moderator</securelist>
<roles>admin,moderator</roles>
<permissions>read</permissions>
<redirect>user.login</redirect>
</rule>
<rule>
<match>url</match>
<whitelist></whitelist>
<securelist>/secured.*</securelist>
<roles>admin,paid_subscriber</roles>
<permissions></permissions>
<redirect>user.pay</redirect>
</rule>
</rules>table property is what table the rules are stored inThe orderBy property is what order by SQL to use, by default it is empty
The sql property is what SQL to execute to retrieve the rules. The default is select * from ${table}
// CB Security
cbSecurity : {
firewall : {
rules : {
provider : {
"source" : "db",
"properties" : {
"dsn" : "myapp",
"sql" : "",
"table" : "securityRules",
"orderBy" : "order asc"
}
}
}
}
}// CB Security
cbSecurity : {
firewall : {
rules : {
provider : {
"source" : "model",
"properties" : {
"model" : "SecurityService",
"method" : "getSecurityRules"
}
}
}
}
}You can also leverage our internal utility methods to get the current request's IP address and hostname. Please remember that the default is to trust the upstream headers and check those first.
getRealIP( trustUpstream : true ) : Get a request's actual IP address
getRealHost( trustUpstream : true ) : Get a request's actual hostname used
// Generate a random password 32 characters in length
cbsecure().createPassword()
// Generate with no symbols and 16 characters
cbsecure().createPassword( length: 16, symbols: false )
// Generate with no numbers and 12 characters
cbsecure().createPassword( length: 12, numbers: false )Security rules in a JSON file
You can place all your security rules inside of a JSON file and then tell CBSecurity where they are:
// CB Security
cbSecurity : {
firewall : {
rules : {
provider : {
"source" : "config/security.json.cfm"
}
}
}
}Then your file can be something like this:
[
{
"whitelist": "user\\.login,user\\.logout,^main.*",
"securelist": "^user\\.*, ^admin",
"match": "event",
"roles": "admin",
"permissions": "",
"redirect": "user.login",
"useSSL": false
},
{
"whitelist": "",
"securelist": "^shopping",
"match": "url",
"roles": "",
"permissions": "shop,checkout",
"redirect": "user.login",
"useSSL": true
}
]CBSecurity stores the secured incoming url so you can relocate the user to it after authenticating.
The security module has the concept of a secured URL which is the actual URL that got intercepted and relocated because of a security exception. This is stored in the request collection as rc._securedURL and in the ColdBox flash memory as _securedURL.
So always remember to use this variable to provide a seamless login experience to your users. You can easily place it in the login form as a hidden field:
#html.startForm( action=prc.xehDoLogin, name="loginForm" )#
<!--- Store the _securedURL so we can use it to relocate -->
#html.hiddenField( name="_securedURL", value=event.getValue('_securedURL','') )#
#html.textfield(name="username",label="Username: ",size="40",required="required",class="textfield",value=prc.rememberMe)#
#html.passwordField(name="password",label="Password: ",size="40",required="required",class="textfield")#
<div id="loginButtonbar">
#html.checkBox(name="rememberMe",value=true,checked=(len(prc.rememberMe)))#
#html.label(field="rememberMe",content="Remember Me ",class="inline")#
#html.submitButton(value=" Log In ",class="buttonred")#
</div>
<br/>
<img src="#prc.cbRoot#/includes/images/lock.png" alt="lostPassword" />
<a href="#event.buildLink( prc.xehLostPassword )#">Lost your password?</a>
#html.endForm()#In your login action you can use the secured URL and relocate appropriately:
function doLogin( event, rc, prc ){
if( cbSecure().authenticate( rc.username, rc.password ) ){
rc._securedURL.len() ? relocate( url : rc._securedURL ) : relocate( "admin.dashboard" )
}
}Here are the configuration settings for the visualizer:
If false then no visualizer, if true then you get a visualizer 🎉
We highly encourage you to ensure the visualizer is ONLY accessible if you have authenticated into your system. By using a secured=true then CBSecurity will incorporate a rule to secure the visualizer for ONLY authenticated users. If you want to be picky, use the securityRule setting.
We also recommend that ONLY certain users have access to the visualizer. You can accomplish this by adding the keys to the security rule created for the visualizer. For example, I only want admins or users with the cbsecurity-visualizer permission to access it.
Please note that the security visualizer can ONLY visualize if you have firewall logs enabled. If no logs are enabled or configured, then the visualizer WILL NOT WORK. Here is a simple logs configuration in the firewall
The dsn key is optional, and CBSecurity will inspect the Application.cfc settings for a default datasource: this.datasource

The provider key is the WireBox ID of the authentication service that must adhere to our interface.
This key is not mandatory and will be automatically filled out from the cbauth user service class by default. This class is used for CBSecurity to know how to retrieve users and validate credentials from whatever storage system you use. This value can be a WireBox ID, and the object must adhere to our interface: cbsecurity.interfaces.IUserService.
This is a convenience setting that tells CBSecurity into which prc (private request context) variable to store the authenticated user on EVERY request. This allows your entire codebase to talk to a single variable for the authenticated user.
cbSecurity Model RetrievalYou can inject our model, or you can use our handy cbsecure() mixin (handlers/layouts/views) and then call the appropriate security functions:
All security methods will call the application's configured Authentication Service to retrieve the currently logged-in user. If the user is not logged in, an immediate NoUserLoggedIn exception will be thrown by all methods.
You can now discover our sections for securing using cbSecurity
// Checks the user has one or at least one permission if the
// permission is a list or array
boolean cbSecurity.has( permission );
// The user must have ALL the permissions
boolean cbSecurity.all( permission );
// The user must NOT have any of the permissions
boolean cbSecurity.none( permission );
// Verify if the passed in user is the same as the logged in user
boolean cbSecurity.sameUser( user );<cfif cbsecure().has( "USER_ADMIN" )>
This is only visible to user admins!
</cfif>
<cfif cbsecure().has( "SYSTEM_ADMIN" )>
<a href="/user/impersonate/#prc.user.getId()#">Impersonate User</a>
</cfif>
<cfif cbsecure().sameUser( prc.user )>
<i class="fa fa-star">This is You!</i>
</cfif>/**
* --------------------------------------------------------------------------
* Security Visualizer
* --------------------------------------------------------------------------
* This is a debugging panel that when active, a developer can visualize security settings and more.
* You can use the `securityRule` to define what rule you want to use to secure the visualizer but make sure the `secured` flag is turned to true.
* You don't have to specify the `secureList` key, we will do that for you.
*/
visualizer : {
"enabled" : false,
"secured" : false,
"securityRule" : {}
},visualizer : {
"enabled" : true,
"secured" : true,
"securityRule" : {
"roles" : "admins",
"permissions" : "cbsecurity-visualizer"
}
}firewall : {
"logs" : {
"enabled" : true,
"dsn" : "myapp",
"schema" : "",
"table" : "cbsecurity_logs",
"autoCreate" : true
}
}cbauth = {
// This is the path to your user object that contains the credential
// validation methods
userServiceClass = "models.UserService"
},/**
* --------------------------------------------------------------------------
* Authentication Services
* --------------------------------------------------------------------------
* Here you will configure which service is in charge of providing authentication for your application.
* By default we leverage the cbauth module which expects you to connect it to a database via your own User Service.
*
* Available authentication providers:
* - cbauth : Leverages your own UserService that determines authentication and user retrieval
* - basicAuth : Leverages basic authentication and basic in-memory user registration in our configuration
* - custom : Any other service that adheres to our IAuthService interface
*/
authentication : {
// The WireBox ID of the authentication service to use which must adhere to the cbsecurity.interfaces.IAuthService interface.
"provider" : "authenticationService@cbauth",
// WireBox ID of the user service to use when leveraging user authentication, we default this to whatever is set
// by cbauth or basic authentication. (Optional)
"userService" : cbauth.userServiceclass,
// The name of the variable to use to store an authenticated user in prc scope on all incoming authenticated requests
"prcUserVariable" : "oCurrentUser"
},"provider" : "SecurityService@contentbox""provider" : "BasicAuthUserService@cbsecurity""prcUserVariable" : "oCurrentUser"// Mixin: Handlers/Layouts/Views
cbsecure()
// Injection
property name="cbSecurity" inject="@cbSecurity"if( cbSecurity.has( "PERM" ) ){
auditUser();
}
if( cbSecurity.sameUser( prc.incomingUser ) ){
// you can change your gravatar
}Luis is passionate about Jesus, tennis, golf, volleyball, and anything electronic. Random Author Facts:
He played volleyball in the Salvadorean National Team at the tender age of 17
The Lord of the Rings and The Hobbit are his favorite books(Geek!)
His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)
He has a geek love for circuits, microcontrollers, and overall embedded systems.
He has, of late (during old age), become a fan of organic gardening.
Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!
“Trust in the LORD with all your heart, and do not lean on your own understanding.” Proverbs 3:5
Configuring CBSecurity for cross site request forgery attacks
CBSecurity ships with the cbsrf module and can be configured in line with the cbsecurity key.
Please note that if any update is made to that module, verify its settings in the module's configuration documentation: https://forgebox.io/view/cbcsrf
By default, this setting is turned off. If you turn it on, then every non-GET request will be verified that it contains a valid incoming csrf token via a header or the incoming rc.
A list of regex patterns that will match against the incoming event. If matched, then that event will be excluded from the auto-verifier.
All csrf tokens have a life span of 30 minutes. But you can control how long they live with this setting.
This setting enables the GET /cbcsrf/generate endpoint to generate csrf tokens for secured users. You can use this endpoint to generate user tokens via AJAX or UI-only applications. Please note that you can pass an optional /:key URL parameter that will generate the token for that specific key.
IMPORTANT: This endpoint is secured via a secured annotation, so make sure the firewall has annotation-driven rules enabled.
The WireBox ID to use for storing the tokens. The default is the CacheStorage@cbstorages object. However, you can use any ColdBox storage or your own as long as it matches the CBStorages API: .
This setting is enabled by default and what it does is that it will rotate a user's secret keys when they login/logout via any authentication service registered with CBSecurity.
January 2023
Dropped Adobe ColdFusion 2016
New JwtAuthValidator instead of mixing concerns with the JwtService. You will have to update your configuration to use this validator instead of the JwtService
All settings have changed. They are not single-level anymore. They are now grouped by functionality. Please see the area for the new approach.
New ability for the firewall to log all action events to a database table.
If enabled, a new visualizer can visualize all settings and firewall events via the log table.
New Basic Auth validator and basic auth user credentials storage system. This will allow you to secure apps where no database interaction is needed or required.
Disable lastAccessTimeouts for JWT CacheTokenStorage BOX-128
Fix spelling of property datasource on queryExecute that was causing a read issue.
Get up and running with CBSecurity in no time!
Leverage CommandBox to install into your ColdBox app:
BoxLang 1+ (Preferred)
Lucee 5+
Adobe 2023+
A database for optional firewall logging
ColdBox 7+ for delegates and basic auth support only
The following mixins are registered once the module is installed:
By default cbsecurity is configured to work with cbauth as the authentication service. You only need to provide a user service class that knows how to connect to your database to retrieve and validate credentials. You can also use the in-built basic authentication users as well.
You can find much more information about cbauth here:
Configuration for basic authentication
The basicAuth key is used to store user credentials that will be used with Basic Authentication and how the passwords are stored in memory.
This is the default algorithm used when hashing the user storage passwords in memory. The default is SHA-512
hashAlgorithm : "SHA-256",Iterates the number of times the hash is computed to create a more computationally intensive hash. The default is 5
This is the in-memory user storage system. It's a struct and each key represents a unique username in the storage system. Each user can then have the following attributes, but in reality, you can add as many attributes as you want.
password - The only mandatory attribute.
firstName
lastName
Now that we have all the pieces in place for JWT, we can now register the JWT validator as our validator of choice: JwtAuthValidator@cbsecurity.
The validator will inspect the incoming requests for valid JWT authorization headers. It will verify their expiration, their required claims, and the user it represents. Once done, it goes in the same rule/annotation security flow that cbsecurity leverages.
Each module can also override its validator via its configuration setting cbsecurity.validator. So if the global validator is something other than JWT but your module REQUIRES JWT validation, then add it in your ModuleConfig.cfc
The JWT validator will discover the incoming JWT token from 3 sources:
authorization header using the bearer token approach
Custom header configured in your settings: cbsecurity.customAuthHeader
Incoming rc variable with the same name as cbsecurity.customAuthHeader
If your rules have the permissions element or your secure annotations have context, then we will treat those as the scopes/permissions to check the user/token must have at validation.
The validator will have the following validation process:
Verify the JWT exists via the authorization header or custom header x-auth-token or incoming rc[ 'x-auth-token' ]
Verify we can decode it
Verify if it has not expired from the token itself
That's it! You can create your rules and annotations just like you used to, but now the validator will ensure valid JWT tokens are passed for those requests.
Delegate yourself!
CBSecurity comes bundled with the following delegate objects that you can use in your user objects and provide them with security capabilities.
Auth@cbsecurity
This delegate enables your application objects to deal with authentication features via delegation.
Authorizable@cbsecurity
This delegate adds authorization capabilities to your objects
JwtSubject@cbsecurity
This delegate adds JWT Subject methods to a target
This delegate enables your application objects to deal with authentication features via delegation.
jwtAuth()
cbSecure()
auth()
This delegate adds authorization capabilities to your objects.
hasPermission()
hasRole()
isLoggedIn()
This delegate adds JWT Subject methods to a target.
getJWTCustomClaims()
getJWTScopes()
Security annotations are used to secure your handler and/or handler actions
CBSecurity also allows you to secure your events via annotations instead of security rules. The setting that controls this security feature is called handlerAnnotationSecurity , which can be set in the
The security module has a tiered approach to annotation security as it will check the handler component first and then the requested action method second. You can apply different security contexts to each level as you see fit.
Please note that the security rules will be run first, and annotations second.
See the diagram below for inspecting security based on annotations:
/**
* --------------------------------------------------------------------------
* CSRF - Cross Site Request Forgery Settings
* --------------------------------------------------------------------------
* These settings configures the cbcsrf module. Look at the module configuration for more information
*/
csrf : {
// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
enableAutoVerifier : false,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [],
// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
// If you do not want expiring tokens, then set this value to 0
rotationTimeout : 30,
// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
enableEndpoint : false,
// The WireBox mapping to use for the CacheStorage
cacheStorage : "CacheStorage@cbstorages",
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : true
},# Latest version
install cbsecurity
# Bleeding Edge
install cbsecurity@be/**
* --------------------------------------------------------------------------
* Basic Auth
* --------------------------------------------------------------------------
* These settings are used so you can configure the hashing patterns of the user storage
* included with cbsecurity. These are only used if you are using the `BasicAuthUserService` as
* your service of choice alongside the `BasicAuthValidator`
*/
basicAuth : {
// Hashing algorithm to use
hashAlgorithm : "SHA-512",
// Iterates the number of times the hash is computed to create a more computationally intensive hash.
hashIterations : 5,
// User storage: The `key` is the username. The value is the user credentials that can include
// { roles: "", permissions : "", firstName : "", lastName : "", password : "" }
users : {}
}cbsecurity = {
validator = "JwtAuthValidator@cbsecurity"
}blockNew event cbSecurity_onFirewallBlock announced whenever the firewall blocks a request into the system with a 403.
DBTokenStorage now rotates using the async scheduler and not direct usage anymore.
Ability to set the cbcsrf module settings into the cbsecurity settings as csrf.
We now default the user service class and the auth token rotation events according to the user authentication service (cbauth, etc.); no need to duplicate work.
New rule-based IP security. You can add a allowedIPs key into any rule and add which IP Addresses are allowed into the match. By default, it matches all IPs.
New rule-based HTTP method security. You can add a httpMethods key into any rule and add which HTTP methods are allowed into the match. By default, it matches all HTTP Verbs.
New securityHeaders configuration to allow a developer to protect their apps from common exploits: XSS, HSTS, Content Type Options, host header validation, IP validation, clickjacking, non-SSL redirection, and much more.
The security firewall now stores the authenticated user according to the prcUserVariable on authenticated calls via preProcess() no matter the validator used
Dynamic Custom Claims: You can pass a function/closure as the value for a custom claim, and it will be evaluated at runtime, passing in the current claims before being encoded
Allow passing in custom refresh token claims to attempt() and fromUser() and refreshToken() : refreshCustomClaims
Added TokenInvalidException and TokenExpiredException to the refreshToken endpoint

roles
permissions
guest()hasAll()
hasNone()
sameUser()
permission can be an array, string or list of the permissions to validate. The user must have at least one of the permissions specified.The message is a custom error message to be used in the message string of the exception thrown.
You also have two more authorization methods that will verify certain permission conditions for you:
There are also cases where you want to execute a piece of code by determining if the user has access to do so. For example, only a USER_ADMIN can change people's roles or you want to filter some data for certain users. For this, we have created the when() method with the following signature:
The permissions is a permission array or list that will be Or'ed
The success is a closure/lambda or UDF that will execute if the permissions validate.
The fail is a closure/lambda or UDF that will execute if the permissions DID not validate, much like an else statement
Both closures/functions takes in a user which is the currently authenticated user, the called in permissions and can return anything.
You can also chain the when() calls if needed, to create beautiful security contexts. So if we go back to our admin examples, we can do something like this:
We have also added the following whenX() methods to serve your needs when evaluating the permissions:
verifyExcludes : [ "stripe\.", "logout" ],rotationTimeout : 60,cacheStorage : "SessionStorage@cbstorages"enableAuthTokenRotator : false/**
* Retrieve the Jwt Auth Service
*/
function jwtAuth()
/**
* Retrieve the CBSecurity Service Object
*/
function cbSecure()hashIterations : 10,users : {
"lmajano" : { password : "test", permissions : "read,write",
"guest" : { password : "guest", permissions : "read" }
}component name="User" delegates="auth@cbSecurity"{
}component name="User" delegates="Authorizable@cbSecurity"{
}component name="User" delegates="JwtSubject@cbSecurity"{
}// Verify the currently logged in user has at least one of those permissions,
// else throw a NotAuthorized exception
cbSecurity.secure( permissions, [message] );
cbsecure().secure( permissions, [message] );// Authorize that the user has ALL of the incoming permissions
cbSecurity.secureAll( permissions, [message] );
// Authorize that the user has NONE of the incoming permissions
cbSecurity.secureNone( permissions, [message] );when( permissions, success, fail )// Lambda approach
( user, permissions ) => {
// your code here
};
// UDF/Closure
function( user, permissions ){
// your code here
}var oAuthor = authorService.getOrFail( rc.authorId );
prc.data = userService.getData();
// Run Security Contexts
cbSecure()
// Only user admin can change to the incoming role
.when( "USER_ADMIN", ( user ) => oAuthor.setRole( roleService.get( rc.roleID ) ) )
// The system admin can set a super admin
.when( "SYSTEM_ADMIN", ( user ) => oAuthor.setRole( roleService.getSystemAdmin() ) )
// Filter the data to be shown to the user
.when( "USER_READ_ONLY", ( user ) => prc.data.filter( ( i ) => !i.isClassified ) )
// Calling with a fail closure
cbSecurity.when(
"USER_ADMIN",
( user ) => user.setRole( "admin" ), //success
( user ) => relocate( "Invaliduser" ) //fail
);// When all permissions must exist in the user
whenAll( permissions, success, fail )
// When none of the permissions exist in the user
whenNone( permissions, success, fail )If you have enabled auto refresh tokens, check out the refresh tokens process.
Verify it has the required claims
If token storage is enabled, verify the token in the permanent storage
Verify the subject (sub) claim and try to retrieve the user it represents
Try to authenticate the user for the request
Verify the subject has the right permissions or that the token has the right scopes attached to it.
If all is valid, then place the token in prc.jwt_token and the payload in prc.jwt_payload
If all is valid, then place the user object in prc.oCurrentUser or the variable of your choice via the cbsecurity.prcUserVariable setting.
Continue or block
The firewall will inspect handlers for a secured annotation. This annotation can be added to the entire handler or to an action method, or both. The default value of the secured annotation is a Boolean true. This means we need a user to be authenticated to access the action.
You can also give the annotation a value, which can be anything you like: A list of roles, a role, a list of permissions, metadata, JSON, etc. Whatever it is, this is called the authorization context, and the user validator must be able to authenticate and authorize the context, or an invalid authorization will occur.
The secured value will be passed to the validator for authorization.
By having the ability to annotate the handler and also the action, you create a cascading security model where they need to be able to access the handler first, and only then will the action be evaluated for access as well.
CBSecurity has many events that you can listen to for an event-driven experience.
The security firewall will announce some interception events when invalid access or authorizations occur within the system:
cbSecurity_onInvalidAuthentication
cbSecurity_onInvalidAuthorization
You will receive the following data in the interceptData struct in each interception call:
ip : The offending IP address
rule : The security rule intercepted or empty if annotations
settings : The firewall settings
With these interceptions you can build a nice auditing system, login tracking and much more.
The received event data has a Boolean key called processActions which defaults to true. This Boolean indicator tells the firewall to process the invalid authentication/authorization procedures. If you change this value to false, then the firewall will do NOTHING because it is expecting for YOU to have done the actions.
If you are using our , then we will announce the following interceptions during JWT usage:
cbSecurity_onJWTCreation
cbSecurity_onJWTInvalidation
cbSecurity_onJWTValidAuthentication
Check them all out in our .
cbauth announces several custom interception points.
preAuthentication
postAuthentication
preLogin
You can use these interception points to change request data or add additional values to session or request scopes. The preAuthentication and postAuthentication events fire during the standard authenticate() method call with a username and password. The preLogin and postLogin events fire during the login() method call. The preLogout and postLogout events fire during the logout() method call.
You can always find the latest interception points here:
The preLogin and postLogin interception points will be called during the course of authenticate(). The order of the calls then are preAuthentication -> preLogin -> postLogin -> postAuthentication.

The CFML Security validator leverages the ColdFusion security functions for authentication and role based authorization.
ColdBox security has had this security validator since version 1, in which it will talk to the ColdFusion engine's security methods to authenticate and authorize users using roles.
All you need to do is use the WireBox ID of CFValidator@cbsecurity in your validator setting:
You can enable token storage in cbsecurity via the tokenStorage setting. By default it is enabled and leverages CacheBox's default cache using a key prefix of cbjwt_ + the token's unique identifier claim of jti.
We recommend that you create a separate provider for the cache.
Basic access authentication is a method for an HTTP user agent (e.g. a web browser) to provide a user name and password when making a request.
CBSecurity supports the concept of HTTP in your ColdBox applications. Please note that this is a quick and easy way to provide security, but not the safest by any means. You have been warned!
In the context of an transaction, basic access authentication is a method for an (e.g. a ) to provide a username and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where credentials is the
settings = {
cbsecurity : {
firewall: {
validator : "JwtAuthValidator@cbsecurity"
}
}
}// Secure the entire handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Do NOT secure the handler
component secured=false{
}
// Same as this, no annotation!
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
}// Secure this handler
component secured="admin,users"{
function index(event,rc,prc) secured="list"{
}
function save(event,rc,prc) secured="write"{
}
}
The storage of keys are great in order to visualize in your application all the registered keys in the system. You can also invalidate keys, as by default if the token does not exist in the storage, it is considered invalid.
You can retrieve the token storage by injection or the helper method:
We ship with two drivers:
cachebox : Leverages any cache registered in CacheBox
db : Leverages a database table to store the keys
cacheName : The cache to use
table : The table to use for storage
schema : A schema to use if the database supports it, else empty
dsn : The datasource to use, defaults to the one set in Application.cfc
autoCreate:true : Autocreate the table if not found
rotationDays:7 : How many days should the expiration be before removal
rotationFrequency:60 : How many minutes should pass before issuing a rotation check
The columns it will create are:
id - identifier
cacheKey - The unique cacke key, indexed
token - The encrypted token
expiration - The expiration
issued - The issue date
subject - The subject identifier
If you would like to create your own token storage, just add your own WireBox ID to the driver, properties and implement the following interface: cbsecurity.interfaces.jwt.IJwtStorage
A security validator object is a simple CFC that implements the following functions
Each validator must return a struct with the following keys:
allow:boolean A Boolean indicator if authentication or authorization was violated
type:stringOf(authentication|authorization) A string that indicates the type of violation: authentication or authorization.
messages:string Info/debug/error messages
Here is a sample validator using permission based security in both rules and annotation context
That's it! Go validate!
The configured authentication service must adhere to our IAuthService interface and the User object must adhere to the IAuthUser interface.
Remember that a validator can exist globally and on a per ColdBox Module le
/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* This is a basic user object that can be used with CBSecurity.
*
* It implements the following interfaces via it's delegates
* - cbsecurity.interfaces.jwt.IJwtSubject
* - cbsecurity.interfaces.IAuthUser
*/
component
accessors ="true"
transientCache="false"
delegates ="
Auth@cbSecurity,
Authorizable@cbSecurity,
JwtSubject@cbSecurity
"
{
/**
* --------------------------------------------------------------------------
* Properties
* --------------------------------------------------------------------------
*/
property name="id";
property name="firstName";
property name="lastName";
property name="username";
property name="password";
property name="permissions";
property name="roles";
/**
* --------------------------------------------------------------------------
* Validation constraints
* --------------------------------------------------------------------------
* https://coldbox-validation.ortusbooks.com/overview/valid-constraints
*/
this.constraints = {
firstName : { required : true, size : "1..255" },
lastName : { required : true, size : "1..255" },
username : { required : true, size : "1..255" },
password : { required : true, size : "1..255" }
};
/**
* --------------------------------------------------------------------------
* Validation profiles
* --------------------------------------------------------------------------
* https://coldbox-validation.ortusbooks.com/overview/validating-constraints/validating-with-profiles
*/
this.constraintProfiles = { "update" : "firstName,lastName,username" };
/**
* --------------------------------------------------------------------------
* Mementifier Serialization
* --------------------------------------------------------------------------
* https://forgebox.io/view/mementifier
*/
this.memento = {
// Default properties to serialize
defaultIncludes : [
"id",
"firstName",
"lastName",
"username",
"permissions",
"roles"
],
// Default Exclusions
defaultExcludes : [],
// Never Include
neverInclude : [ "password" ]
};
/**
* --------------------------------------------------------------------------
* Population Control
* --------------------------------------------------------------------------
* https://coldbox.ortusbooks.com/readme/release-history/whats-new-with-7.0.0#population-enhancements
*/
this.population = {
include : [], // if empty, tries to include them all
exclude : [ "permissions", "roles" ] // These are not mass assignable
}
/**
* Constructor
*/
function init(){
variables.id = "";
variables.firstName = "";
variables.lastName = "";
variables.username = "";
variables.password = "";
variables.permissions = [];
variables.roles = [];
return this;
}
/**
* Set roles into the object
*
* @roles array or list of roles
*/
User function setRoles( roles ){
if ( isSimpleValue( arguments.roles ) ) {
arguments.roles = listToArray( arguments.roles );
}
variables.roles = arguments.roles;
return this;
}
/**
* Set permissions into this object
*
* @permissions array or list of permissions
*/
User function setPermissions( permissions ){
if ( isSimpleValue( arguments.permissions ) ) {
arguments.permissions = listToArray( arguments.permissions );
}
variables.permissions = arguments.permissions;
return this;
}
/**
* Verify if this is a valid user or not
*/
boolean function isLoaded(){
return ( !isNull( variables.id ) && len( variables.id ) );
}
}
property name="tokenStorage" inject="DBTokenStorage@cbsecurity";
property name="tokenStorage" inject="CacheTokenStorage@cbsecurity";
jwtAuth().getTokenStorage()interface{
/**
* Configure the storage by passing in the properties
*
* @return JWTStorage
*/
any function configure( required properties );
/**
* Set a token in the storage
*
* @key The cache key
* @token The token to store
* @expiration The token expiration
*
* @return JWTStorage
*/
any function set( required key, required token, required expiration );
/**
* Verify if the passed in token key exists
*
* @key The cache key
*/
boolean function exists( required key );
/**
* Retrieve the token via the cache key, if the key doesn't exist a TokenNotFoundException will be thrown
*
* @key The cache key
* @defaultValue If not found, return a default value
*
* @throws TokenNotFoundException
*/
any function get( required key, defaultValue );
/**
* Invalidate/delete one or more keys from the storage
*
* @key A cache key or an array of keys to clear
*
* @return JWTStorage
*/
any function clear( required any key );
/**
* Clear all the keys in the storage
*
* @async Run in a separate thread
*
* @return JWTStorage
*/
any function clearAll( boolean async=false );
/**
* Retrieve all the jwt keys stored in the storage
*/
array function keys();
/**
* The size of the storage
*/
numeric function size();
}moduleSettings = {
cbSecurity = {
validator = "SecurityService"
}
}/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* All security validators must implement the following methods
*/
interface{
/**
* This function is called once an incoming event matches a security rule.
* You will receive the security rule that matched and an instance of the ColdBox controller.
*
* You must return a struct with three keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue
* - messages:string Info/debug messages
*
* @return { allow:boolean, type:string(authentication|authorization), messages:string }
*/
struct function ruleValidator( required rule, required controller );
/**
* This function is called once access to a handler/action is detected.
* You will receive the secured annotation value and an instance of the ColdBox Controller
*
* You must return a struct with three keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue
* - messages:string Info/debug messages
*
* @return { allow:boolean, type:string(authentication|authorization), messages:string }
*/
struct function annotationValidator( required securedValue, required controller );
}struct function ruleValidator( required rule, required controller ){
return permissionValidator( rule.permissions, controller, rule );
}
struct function annotationValidator( required securedValue, required controller ){
return permissionValidator( securedValue, controller );
}
private function permissionValidator( permissions, controller, rule ){
var results = { "allow" : false, "type" : "authentication", "messages" : "" };
var user = security.getCurrentUser();
// First check if user has been authenticated.
if( user.isLoaded() AND user.isLoggedIn() ){
// Do we have the right permissions
if( len( arguments.permissions ) ){
results.allow = user.checkPermission( arguments.permission );
results.type = "authorization";
} else {
results.allow = true;
}
}
return results;
}validatorResults : The validator results
annotationType : The annotation type intercepted, handler or action or empty if rule driven
processActions : A Boolean indicator that defaults to true. If you change this to false, then the interceptor won't fire the invalid actions. Usually this means, you manually will do them.
cbSecurity_onJWTInvalidUser
cbSecurity_onJWTInvalidClaims
cbSecurity_onJWTExpiration
cbSecurity_onJWTStorageRejection
cbSecurity_onJWTValidParsing
cbSecurity_onJWTInvalidateAllTokens
postLogin
preLogout
postLogout
A container for user authentication and login code. The body of the tag runs only if the user is not logged in. When using application-based security, you place code in the body of the cflogin tag to check the user-provided ID and password against a data source, LDAP directory, or other repository of login identification. The body of the tag includes a cfloginuser tag (or a ColdFusion page that contains a cfloginuser tag) to establish the authenticated user's identity in ColdFusion.
Identifies (logs in) a user to ColdFusion. Specifies the user's ID, password, and roles. This tag is typically used inside a cflogin tag. The cfloginuser tag requires three attributes, name, password, and roles, and does not have a body. The roles attribute is a comma-delimited list of role identifiers to which the logged-in user belongs. All spaces in the list are treated as part of the role names, so you should not follow commas with spaces.While the user is logged-in to ColdFusion, security functions access the user ID and role information.
Logs out the current user. Removes knowledge of the user ID and roles from the server. If you do not use this tag, the user is automatically logged out as described in Logging out users in .The cflogout tag does not take any attributes, and does not have a body.
Authenticates a user name and password against the NT domain on which ColdFusion server is running, and optionally retrieves the user's groups.
If you include a roles attribute, the function executes only when there is a logged-in user who belongs to one of the specified roles.
Returns True if the current user is a member of the specified role.
Returns the ID of the currently logged-in user.This tag first checks for a login made with cfloginuser tag. If none exists, it checks for a web server login (cgi.remote_user.
For more information about cflogin, cfloginuser and cflogout, please visit the docs http://cfdocs.org/security-functions
The configured authentication service must adhere to our IAuthService interface and the User object must adhere to the IAuthUser interface.
Remember that a validator can exist globally and on a per ColdBox Module level.
:The first step is configuring your application to use basic authentication as the validator of choice. We will configure two things:
Validator: BasicAuthValidator@cbsecurity
Basic auth settings: Where you configure users, passwords, roles, permissions, and encryption
CBSecurity allows you to use basic authentication with ANY authentication service.
This is the most basic configuration where we register a single user and tell the firewall to use the basic auth validator. Since the default authentication service is cbauth I don't have to register it. Finally, since CBSecurity detects the BasicAuthValidatorand no registered user class, it will register the BasicAuthUserService as well for you.
You can explicitly set the UserServiceClass to be BasicAuthUserService@cbsecurity if you wanted to.
All I have to do now is create security rules or annotations, and CBSecurity will leverage the browser's Basic Authentication Prompt when those resources are trying to be accessed. Once you put in your credentials, it will verify them against the registered users in the basicAuth configuration dictionary.
Since Basic Authentication ONLY focuses on login, logout is left out of the equation. In CBSecurity, we have created a special event so you can securely log out users from basic authentication, which you can hit with ANY HTTP verb.
This will call the logout method of the authentication service and set the following HTTP headers for you so your session can be rotated:
Ultimately, you can close your browser too.
ColdBox also supports the concept of basic authentication retrieval since the early version 2 days. ColdBox can detect, parse and give you a struct of username and password by leveraging the request context's getHTTPBasicCredentials() method.
ColdBox Security supports the concept of refresh tokens alongside the normal JWT access tokens. Let's start exploring this feature in detail.
A refresh token is a credential artifact that lets a client application get new access tokens without having to ask the user to log in again. Access tokens may be valid for a short amount of time. Once they expire, client applications can use a refresh token to "refresh" the access token.
The client application can get a new access token as long as the refresh token is valid and unexpired. Consequently, a refresh token that has a very long lifespan could theoretically give infinite power to the token bearer to get a new access token to access protected resources anytime. The bearer of the refresh token could be a legitimate user or a malicious user.
component extends="coldbox.system.Interceptor"{
function cbSecurity_onInvalidAuthentication( event, interceptData ){
// do what you like here
}
function cbSecurity_onInvalidAuthorization( event, interceptData ){
// do what you like here
}
}cbsecurity = {
firewall : {
validator = "CFValidator@cbsecurity"
}
}component{
function login( event, rc, prc ){
event.setView( "security/login" );
}
function doLogin( event, rc, prc ){
cflogin(
idletimeout=getSetting( "LoginTimeout" ),
applicationtoken=getSetting( "AppName" ),
cookiedomain='myapp.com'
){
cfoauth(
type = "Google",
clientid = "YOUR_CLIENT_ID",
secretkey = "YOUR_GOOGLE_CLIENTSECRET",
redirecturi = "YOUR_CALLBACK_URI",
result = "res",
scope = "YOUR_SCOPES",
state = "cftoken=#cftoken#"
);
cfloginuser(
name = "#res.other.email#",
password = "#res.access_token#",
roles = "user"
);
}
}
function doLogout( event, rc, prc ){
cflogout();
relocate( "security.login" );
}
}cbsecurity : {
basicAuth : {
users : {
"lmajano" : { password : 'test', permissions : "", roles : "admin" }
}
},
firewall : {
// Global Relocation when an invalid access is detected, instead of each rule declaring one.
"invalidAuthenticationEvent" : "main.index",
// Default invalid action: override or redirect when an invalid access is detected, default is to redirect
"defaultAuthenticationAction" : "redirect",
// Global override event when an invalid access is detected, instead of each rule declaring one.
"invalidAuthorizationEvent" : "main.index",
// Default invalid action: override or redirect when an invalid access is detected, default is to redirect
"defaultAuthorizationAction" : "redirect",
// Firewall Validator
"validator" : "BasicAuthValidator@cbsecurity"
}
}/cbsecurity/basicauth/logoutevent
.setHTTPHeader( name = "WWW-Authenticate", value = "basic realm='Please enter your credentials'" )
.setHTTPHeader( name = "Cache-Control", value = "no-cache, must-revalidate, max-age=0" )
.renderData( data = "<h1>Logout Successful!</h1>", statusCode = 401 );function preHandler( event, action, eventArguments ){
var authDetails = event.getHTTPBasicCredentials();
if( !securityService.authenticate( authDetails.username, authDetails.password ) ) {
event.renderData( type="JSON", data={ message = 'Please check your credentials' }, statusCode=401, statusMessage="You're not authorized to do that");
}
}In the jwt section of the cbsecurity configuration you will have the following settings dealing with refresh tokens (Please note that the other jwt configurations are also mandatory)
This setting is used to turn on the refresh capabilities of the JWT Service. If this remains false, then exceptions will be thrown when trying to use refresh capabilities.
The default time refresh tokens expire in. The default is 7 days or 10080 minutes
The header to inspect for refresh tokens for automatic refreshment or our refresh endpoints. The default is x-refresh-token
If you enable the auto refresh validator setting, then cbsecurity will try to auto-refresh expired access tokens via the Validator security events. These events fire when a rule is detected or a secured annotation is detected.
If enabled, the REST cbsecurity/refreshToken endpoint will be available for the application, so users can refresh their tokens.
You must enable the setting (enableRefreshTokens) in order for the following methods to return a struct of tokens. If not, only the access token will be returned as a string.
attempt( username, password ):struct
fromUser( user, customClaims ):struct
The returned struct will contain the access and refresh tokens:
This is the same procedure for creating access tokens, but now you get a struct of tokens instead of a single access token.
You can refresh tokens manually by using the refreshToken( token, customClaims ) method on the JwtService object. You can pass a valid refresh token to be used for refreshment or pass none and the token will be inspected from the headers or incoming rc using the x-refresh-token value or whatever you setup as your customRefreshHeader setting.
Here is the signature for the refresh method:
customClaims where added in v2.15.0
Important:
Please note that the currently used refresh token will be invalidated and rotated for you automatically. This is a security feature called refresh token rotation, where the refreshed token is automatically rotated for you upon refresh usage.
If you have enabled the refresh token setting (enableRefreshEndpoint) then you will also have access to the POST > /cbsecurity/refreshtoken API endpoint.
This endpoint is used for applications to refresh their access tokens using the refresh token received when authenticating in the application.
This endpoint must be executed with a POST and you will need to pass in your refresh token via the following header: x-refresh-token or rc form variable of the same name. If valid, the response data will contain two new tokens for you:
Important:
Please note that the currently used refresh token will be invalidated and rotated for you automatically. This is a security feature called refresh token rotation, where the refreshed token is automatically rotated for you upon refresh usage.
CBSecurity by default provides you with refresh token rotation every time you want to refresh your access token. This guarantees that every time an application exchanges a refresh token for an access token, a NEW refresh token is returned as well. The old refresh token is invalidated and can no longer be used.
Therefore, you no longer have a long-lived refresh token that could provide illegitimate access to resources if it ever becomes compromised or leaked. The threat of unauthorized access is reduced as refresh tokens are continually exchanged and invalidated.
CBSecurity has the ability to refresh access tokens automatically for you when calling any secure resource that is protected by the JWT Validator. All you have to do is send in both tokens via the appropriate headers and enable the autoRefreshValidator setting:
access token
bearer token or
x-auth-token
refresh token
x-refresh-token
If the access token has expired or is invalid or missing and the x-refresh-token was passed and is valid, then the access token will be re-generated, the refresh token will be rotated, the request will continue as normal and two new response headers will be sent back to the calling application.
x-auth-token : refreshed access token
x-refresh-token : new refresh token
The calling application can monitor if those two response headers are sent and save them appropriately.
jwt : {
...
// If true, enables refresh tokens, token creation methods will return a struct instead of just an access token string
// e.g. { access_token: "", refresh_token : "" }
"enableRefreshTokens" : false,
// The default expiration for refresh tokens in minutes, defaults to 7 days
"refreshExpiration" : 10080,
// The custom header to inspect for refresh tokens
"customRefreshHeader" : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the `customRefreshHeader` and `customAuthHeader`
"enableAutoRefreshValidator" : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
"enableRefreshEndpoint" : false
}{
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}var newTokens = jwtService.refreshToken();
var newTokens = jwtService.refreshToken( storedRefreshToken );/**
* Manually refresh tokens by passing a valid refresh token and returning two new tokens:
* <code>{ access_token : "", refresh_token : "" }</code>
*
* @refreshToken A refresh token
* @customClaims A struct of custom claims to apply to the new tokens
*
* @throws RefreshTokensNotActive If the setting enableRefreshTokens is false
* @throws TokenExpiredException If the token has expired or no longer in the storage (invalidated)
* @throws TokenInvalidException If the token doesn't verify decoding
* @throws TokenNotFoundException If the token cannot be found in the headers
*
* @return A struct of { access_token : "", refresh_token : "" }
*/
struct function refreshToken( token = discoverRefreshToken(), struct customClaims = {} ){
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}The issuer authority for the tokens is placed in the iss claim of the token. If empty, we will use the event.buildLink() to create the issuer. By default, our validators also check that tokens are created by the same issuer.
The secret key is used to sign the JWT tokens. By default, it will try to load an environment variable called JWT_SECRET , if that setting is also empty, we will auto-generate a secret token that will last as long as the ColdFusion application scope lasts. So technically, your secret will rotate only if a secret is not specified.
Also, this key is ignored in modules. To specify a fixed key to be used in your modules, you will have to configure it by adding a cbsecurity key settings in the moduleSettings structure within the config/Coldbox.cfc.
Your secret key will auto-rotate every application scope rotation. Please note that all tokens used after that scope rotation will automatically become invalid.
Please note that we use the jwt-cfml library for encoding/decoding tokens. Please refer to it's documentation in order to leverage RS and ES algorithms with certificates.
By default, our jwt services will look into the authorization header for a bearer token. However, it can also look in a custom header by this name, which defaults to x-auth-token. Finally, if not found, it will also look into the rc scope for a rc[ 'x-auth-token' ] as well.
The default expiration in minutes for the JWT tokens. Defaults to 60 minutes
The encryption algorithm to use for the tokens. The default is HS512, but the available ones for are:
HS256
HS384
HS512
RS256
RS384
RS512
ES256
ES384
ES512
In the case of the RS and ES algorithms, asymmetric keys are expected to be provided in unencrypted PEM or JWK format (in the latter case, first deserialize the JWK to a CFML struct). When using PEM, private keys must be encoded in PKCS#8 format.
If your private key is not currently in this format, conversion should be straightforward:
When decoding tokens, either a public key or certificate can be provided. (If a certificate is provided, the public key will be extracted.)
This is an array of claim names that each token MUST have in order to be authenticated. If a token comes in but does not have these claims in the payload structure, it will be deemed invalid.
By default, our JWT services will store tokens in CacheBox for you in order to be able to invalidate them. We ship with two providers for token storage: db and cachebox.
By default, the token storage is enabled.
The key prefix to use when storing the keys in permanent storage. Defaults to cbjwt_
The driver to use. It can be either db or cachebox or your own WireBox Id for using custom storage.
A struct of properties to configure each storage with.
Refresh tokens have several configuration items; check them out in our refresh token configuration section.


The JWT Services will announce some key events for you to listen to
cbSecurity_onJWTCreation - Whenever a new token is generated for a user
cbSecurity_onJWTInvalidation - Whenever an invalidation occurs for a token
cbSecurity_onJWTValidAuthentication - Whenever a valid JWT token is parsed, tested and authenticated with the authentication services
cbSecurity_onJWTInvalidUser - When trying to find the token's subject and the user service returns null or not a valid user
cbSecurity_onJWTInvalidClaims - When the parsed token does not adhere to the required claims
cbSecurity_onJWTExpiration - When the parsed token has expired
cbSecurity_onJWTStorageRejection - When the parsed token is valid but cannot be found in the permanent storage
cbSecurity_onJWTValidParsing - When the parsed token has passed all validation procedures but has NOT been authenticated yet.
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
This event has the following data in the interceptData struct
cbsecurity : {
/**
* --------------------------------------------------------------------------
* Json Web Tokens Settings
* --------------------------------------------------------------------------
* Here you can configure the JWT services for operation and storage. In order for your firewall
* to leverage JWT authentication/authorization you must make sure you use the `JwtAuthValidator` as your
* validator of choice; either globally or at the module level.
*/
jwt : {
// The issuer authority for the tokens, placed in the `iss` claim
issuer : "",
// The jwt secret encoding key, defaults to getSystemEnv( "JWT_SECRET", "" )
// This key is only effective within the `config/Coldbox.cfc`. Specifying within a module does nothing.
secretKey : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well.
customAuthHeader : "x-auth-token",
// The expiration in minutes for the jwt tokens
expiration : 60,
// If true, enables refresh tokens, token creation methods will return a struct instead
// of just the access token. e.g. { access_token: "", refresh_token : "" }
enableRefreshTokens : false,
// The default expiration for refresh tokens, defaults to 30 days
refreshExpiration : 10080,
// The Custom header to inspect for refresh tokens
customRefreshHeader : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
enableAutoRefreshValidator : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
enableRefreshEndpoint : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
algorithm : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
requiredClaims : [] ,
// The token storage settings
tokenStorage : {
// enable or not, default is true
"enabled" : true
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
cacheName : "default"
}
}
}
}$ openssl pkcs8 -topk8 -nocrypt -in privatekey.pem -out privatekey.pk8
Key
Description
token
The JWT token
payload
The payload that was used to create it
user
The user it belongs to
Key
Description
token
The JWT token that was invalidated
Key
Description
token
The JWT token that was parsed
payload
The payload that was decoded
user
The authenticated user
Key
Description
token
The JWT token that was parsed
payload
The JWT payload that was parsed
Key
Description
token
The JWT token that was parsed
payload
The JWT payload that was parsed
Key
Description
token
The JWT token that was parsed
payload
The JWT payload that was parsed
Key
Description
token
The JWT token that was parsed
payload
The JWT payload that was parsed
Key
Description
token
The JWT token that was parsed
payload
The JWT payload that was parsed
component extends="coldbox.system.Interceptor"{
function cbSecurity_onJWTCreation( event, interceptData ){
// do what you like here
}
}
CBSecurity can apply security rules to incoming events.
We have seen by now that rules can be defined in disk, databases, or created at runtime. These security rules all share a common anatomy and processing; let's explore it.
A struct models each rule with keys in it internally in CBSecurity. So no matter where these rules come from, at the end of the day, they are registered as an array of structs internally.
The only required key is the secureList which is what you are trying to secure. The rest are optional.
Please note that you can add as many extra keys as you like to your security rules structure, which can contain much more context and information for the validators to use for validation. These are the ones we suggest you add and are used internally.
Please remember that by default, the secure and white lists are evaluated as regular expressions. You can turn that off in your
When processing rules, it is essential to realize these rules are stored as an array that will be processed in order, so make sure your more specific rules will be processed before the more generic ones.
As we saw from the overview and our configuration sections. We can declare the default actions for authorizations and authentication issues and to which events/URLs to go if that happens. There can be a time when you can override those global/module settings directly within a rule. Let's explore these overrides:
If you add a redirect element, then you will explicitly override the global/module setting, and if a match is made, a redirect will occur for the event registered.
If you add an overrideEvent element, then you will explicitly override the global/module setting, and an event override will occur.
If you add an action element, then you will be explicitly overriding the global/module setting, and the action will be based on this value (override or event or block)
If a rule has a white list, then it means that you can declare what are the exceptions to ALLOW if the incoming URL/event was matched against the securedList. This is a great way to say, hey, secure all but allow the following events:
Please note: if a rule has a white list, it only applies to the current rule. So if the whitelist matches, it the current rule is skipped, and the process continues to the next rule.
Sometimes you want to make sure ALL events are secured, except for the ones specified, such as login events. If you add new functionality to your app it is easy to forget a new rule. To prevent unwanted access you could specify a LAST rule, which matches ALL event but NO permission at all. In that case you have to add a whitelist for all events which should still pass, for example:
Configuring the security response headers and features
CBSecurity comes bundled with tons of security response features to help developers be secure-minded about their applications. Here are the defaults for configuring the security headers in CBSecurity.
This boolean flag tells CBSecurity whether to inspect x-forwarded- headers FIRST instead of traditional host/IP headers. If you trust your proxies, then turn this setting to true.
The Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for data theft, site defacement, and malware distribution.
By default, this policy is disabled as it requires a custom policy to be written according to your needs. Once you have a policy available, you can add it to the configuration.
The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in the Content-Type headers should be followed and not be changed. This produces the following header => X-Content-Type-Options: nosniff
You can fill out this struct with the custom headers you would like to send out on EVERY request. The header value can be a simple value to return always or a closure/lambda that will be executed at runtime and the value sent on every request.
Please note that the closure accepts the incoming event, rc, and prc variables.
The X-Frame-Options response header can be used to indicate whether or not a browser should be allowed to render a page in a , , or . Sites can use this to avoid attacks by ensuring that their content is not embedded into other sites. The default value ColdBox uses is SAMEORIGIN which allows iframes and embeds from the same origin. The available values are: SAMEORIGIN OR DENY.
The HTTP Strict-Transport-Security response header (often abbreviated as HSTS) informs browsers that the site should only be accessed using HTTPS and that any future attempts to access it using HTTP should automatically be converted to HTTPS. Here are the defaults we use in CBSecurity:
This configuration setting can restrict access to your application for ONLY a specific list of hosts. This prevents host spoofing. If an invalid host is detected, a 401 Not Authorized response will be sent back to the user. This setting is disabled by default.
This configuration setting can restrict access to your application for ONLY a specific list of IP addresses. This prevents IP spoofing. If an invalid IP is detected, then a 401 Not Authorized response will be sent back to the user. This setting is disabled by default.
Please note that as of now, a full IP address must be used.
The Referrer-Policy HTTP header controls how much referrer information (sent with the Referer header) should be included with requests. Aside from the HTTP header, you can set this policy in HTML. This setting is enabled by default with a policy of same-origin.
Here are some available policies:
Detect if the incoming requests are NON-SSL and redirect with SSL alongside any incoming query strings and host information if enabled. By default, this setting is disabled.
Some browsers have built-in support for filtering out reflected XSS attacks. Not foolproof, but it assists in XSS protection. By default, it is enabled and a block mode is produced.
This feature set is provided by the cbcsrf module.
Since version 2.4.x we have added the cbcsrf module as a dependency of cbSecurity. Below is how you can use it:
Below are the settings you can use for this module. Remember you must create the cbcsrf struct in your ColdBox.cfc under the moduleSettings structure:
This module will add the following UDF mixins to handlers, interceptors, layouts and views:
csrfToken() : To generate a token, using the default or a custom key
csrfVerify() : Verify a valid token or not
csrf() : To generate a hidden field (csrf
Here are the method signatures:
The module also registers the following mapping in WireBox: @cbcsrf so you can call our service model directly.
By default, the module is configured to rotate all user csrf tokens every 30 minutes. This means that every token that gets created has a maximum life-span of {rotationTimeout} minutes. If you do NOT want the tokens to EVER expire during the user's logged in session, then use the value of 0 zero.
It is recommended to rotate your keys often, in case your token get's compromised.
We have provided several methods to rotate or clear out all of a user's tokens. If you are using cbAuth as your module of choice for authentication, then we will listen to logins and logouts and rotate the keys for you if you have enabled the enableAuthTokenRotator setting.
If you are NOT using cbAuth then we recommend you leverage the csrfRotate() mixin or the cbsrf.rotate() method on the @cbsrf model and do the manual rotation yourself.
Below is a simple example of manually verifying tokens in your handlers:
We have included an interceptor that if loaded will verify all incoming requests to make sure the token has been passed or it will throw an exception.
The settings for this feature are:
You can also register an array of regular expressions that will be tested against the incoming event and if matched, it will allow the request through with no verification.
The verification process is as follows:
If we are doing an integration test, then skip verification
If the incoming HTTP Method is a get,options or head skip verification
If the incoming event matches any of the verifyExcludes setting, then skip verification
Please note that this verifier will check the following locations for the token:
The request collection (rc) via the cbcsrf key
The request HTTP header (x-csrf-token) key
skipCsrf AnnotationYou can also annotate your event handler actions with a skipCsrf annotation and the verifier will also skip the verification process for those actions.
/cbcsrf/generate EndpointThis module also allows you to turn on the generation HTTP endpoint via the enableEndpoint boolean setting. When turned on the module will register the following route: GET /cbcsrf/generate/:key?. You can use this endpoint to generate tokens for your users via AJAX or UI only applications. Please note that you can pass an optional /:key URL parameter that will generate the token for that specific key.
This endpoint should be secured, so we have annotated it with a secured annotation so if you are using cbSecurity or cbGuard this endpoint will only be available to logged in users.
{
"id" : created automatically as a UUID,
// A list of white list events or Uri's
"whiteList" : "",
// A list of secured list events or Uri's
"secureList" : "",
// Match the event or a url
"match" : "event",
// Attach a list of roles to the rule
"roles" : "",
// Attach a list of permissions to the
"permissions" : "", rule
// If rule breaks, and you have a redirect it will redirect here
"redirect" : "",
// If rule breaks, and you have an event, it will override it
"overrideEvent" : "",
// Force SSL,
"useSSL" : false,
// The action to use (redirect|override|block) when no redirect or overrideEvent is defined in the rule.
"action" : "",
// metadata we can add so mark rules that come from modules
"module" : "",
// Match all HTTP methods or particular ones as a list
"httpMethods" : "*",
// The rule only matches if the IP list matches. It can be a list of IPs to match.
"allowedIPs" : "*"
}/**
* --------------------------------------------------------------------------
* Security Headers
* --------------------------------------------------------------------------
* This section is the way to configure cbsecurity for header detection, inspection and setting for common
* security exploits like XSS, ClickJacking, Host Spoofing, IP Spoofing, Non SSL usage, HSTS and much more.
*/
securityHeaders : {
// If you trust the upstream then we will check the upstream first for specific headers
"trustUpstream" : false,
// Content Security Policy
// Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks,
// including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to
// site defacement, to malware distribution.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
"contentSecurityPolicy" : {
// Disabled by defautl as it is totally customizable
"enabled" : false,
// The custom policy to use, by default we don't include any
"policy" : ""
},
// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in
// the Content-Type headers should be followed and not be changed => X-Content-Type-Options: nosniff
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
"contentTypeOptions" : { "enabled" : true },
"customHeaders" : {
// Name : value pairs as you see fit.
},
// Disable Click jacking: X-Frame-Options: DENY OR SAMEORIGIN
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
"frameOptions" : { "enabled" : true, "value" : "SAMEORIGIN" },
// HTTP Strict Transport Security (HSTS)
// The HTTP Strict-Transport-Security response header (often abbreviated as HSTS)
// informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it
// using HTTP should automatically be converted to HTTPS.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security,
"hsts" : {
"enabled" : true,
// The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS, 1 year is the default
"max-age" : "31536000",
// See Preloading Strict Transport Security for details. Not part of the specification.
"preload" : false,
// If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
"includeSubDomains" : false
},
// Validates the host or x-forwarded-host to an allowed list of valid hosts
"hostHeaderValidation" : {
"enabled" : false,
// Allowed hosts list
"allowedHosts" : ""
},
// Validates the ip address of the incoming request
"ipValidation" : {
"enabled" : false,
// Allowed IP list
"allowedIPs" : ""
},
// The Referrer-Policy HTTP header controls how much referrer information (sent with the Referer header) should be included with requests.
// Aside from the HTTP header, you can set this policy in HTML.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
"referrerPolicy" : { "enabled" : true, "policy" : "same-origin" },
// Detect if the incoming requests are NON-SSL and if enabled, redirect with SSL
"secureSSLRedirects" : { "enabled" : false },
// Some browsers have built in support for filtering out reflected XSS attacks. Not foolproof, but it assists in XSS protection.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection,
// X-XSS-Protection: 1; mode=block
"xssProtection" : { "enabled" : true, "mode" : "block" }
}csrfField() : Generate a random token in a hidden form element and javascript that will refresh the page automatically when the token expires
csrfRotate() : To wipe and rotate the tokens for the user
If the action is marked with a skipCsrf annotation, then skip verification
If no rc.csrf exists and no x-csrf-token header exists, throw a
TokenNotFoundException exception
If the token is invalid then throw a TokenMismatchException exception
moduleSettings = {
cbcsrf : {
// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
enableAutoVerifier : false,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [],
// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
// If you do not want expiring tokens, then set this value to 0
rotationTimeout : 30,
// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
enableEndpoint : false,
// The WireBox mapping to use for the CacheStorage
cacheStorage : "CacheStorage@cbstorages",
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : false
}
};/**
* Provides a random token and stores it in the coldbox cache storages. You can also provide a specific key to store.
*
* @key A random token is generated for the key provided.
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return csrf token
*/
string function csrfToken( string key='', boolean forceNew=false )
/**
* Validates the given token against the same stored in the session for a specific key.
*
* @token Token that to be validated against the token stored in the session.
* @key The key against which the token be searched.
*
* @return Valid or Invalid Token
*/
boolean function csrfVerify( required string token='', string key='' )
/**
* Generate a random token and build a hidden form element so you can submit it with your form
*
* @key A random token is generated for the key provided.
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return HTML of the hidden field (csrf)
*/
string function csrf( string key='', boolean forceNew=false )
/**
* Generate a random token in a hidden form element and javascript that will refresh the page automatically when the token expires
*
* @key A random token is generated for the key provided. CFID is the default
* @forceNew If set to true, a new token is generated every time the function is called. If false, in case a token exists for the key, the same key is returned.
*
* @return HTML of the hidden field (csrf)
*/
string function csrfField( string key='', boolean forceNew=false )
/**
* Clears out all csrf token stored
*/
function csrfRotate()component{
property name="cbcsrf" inject="@cbcsrf";
}moduleSettings = {
cbcsrf : {
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : true
}
};component{
function doLogin( event, rc, prc ){
if( valid login ){
// login user
csrfRotate();
}
}
function logout( event, rc, prc ){
csrfRotate();
}
}component extends="coldbox.system.EventHandler"{
function signUp( event, rc, prc ){
// Store this in a hidden field in the form
prc.token = csrfGenerate();
event.setView( "registration/signup" );
}
function signUpProcess( event, rc, prc ){
// Verify CSFR token from form
if( csrfVerify( rc.token ?: '' ) {
// save form
} else {
// Something isn't right
relocate( 'handler.signup' );
}
}
}cbcsrf : {
// Enable the verifier
enableAutoVerifier : true,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [
]
}component{
function doTestSave( event, rc, prc ) skipCsrf{
}
}*
Match all HTTP methods or particular ones as a list. By default, it matches all HTTP Methods.
id
uuid
createUUID()
The internal ID of the rule. We automatically assign a UUID to the rule upon registration.
match
event or URL
Determines if it needs to match the incoming URL or the incoming event. By default it matches the incoming event.
module
string
empty
The name of the module this rule belongs to. Empty if none is discovered.
overrideEvent
string
The event to override using ColdBox's event.overrideEvent() if the user if not authenticated or authorized
permissions
string
A comma delimited list of permissions that can access these secure events
redirect
string
An event or route to redirect if the user is not authenticated or authorized
roles
string
A comma delimited list of roles that can access these secure events
securelist
string
A comma delimited list of events or regex patterns to secure
whitelist
string
A comma delimited list of events or regex patterns to whitelist or to bypass security on if a match is made on the secureList
useSSL
boolean
If true, force SSL, else use whatever the request protocol is
action
string
empty
The action to use (redirect or override or block) when no explicit overrideEvent or redirect elements are defined. If not set, then we use the global settings.
allowedIPs
string
*
The rule only matches if the IP list matches. It can be a list of IPs to match. By default, it matches all incoming IPs.
httpMethods
string
{
"secureList" : "*",
"redirect" : "mysecret.event"
}{
"secureList" : "*",
"overrideEvent" : "main.onInvalidEvent"
}{
"secureList" : "^api.*",
"action" : "override"
}{
"secureList" : ".*",
"whitelist : "^login"
}{
"secureList" : ".*",
"whitelist" : "login",
"permissions" : "nonExistingPermission"
}"contentSecurityPolicy" : {
// Disabled by defautl as it is totally customizable
"enabled" : true,
// The custom policy to use, by default we don't include any
"policy" : "default-src 'self' *.example.com; img-src *"
},"contentTypeOptions" : { "enabled" : true },customHeaders : {
"x-mvc" : "ColdBox",
"x-runtime-timestamp" : (event,rc,prc) => now()
}"frameOptions" : { "enabled" : true, "value" : "SAMEORIGIN" },"hsts" : {
"enabled" : true,
// The time, in seconds, that the browser should remember that a site is only to
// be accessed using HTTPS, 1 year is the default
"max-age" : "31536000",
// See Preloading Strict Transport Security for details. Not part of the specification.
"preload" : false,
// If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
"includeSubDomains" : false
},// Validates the host or x-forwarded-host to an allowed list of valid hosts
"hostHeaderValidation" : {
"enabled" : true,
// Allowed hosts list
"allowedHosts" : "www.coldbox.org,coldbox.org"
},// Validates the host or x-forwarded-host to an allowed list of valid hosts
"ipValidation" : {
"enabled" : true,
// Allowed hosts list
"allowedHosts" : "127.0.0.1,98.98.98.98"
},"referrerPolicy" : {
"enabled" : true,
"policy" : "same-origin"
},Policy: no-referrer
Policy: no-referrer-when-downgrade
Policy: origin
Policy: origin-when-cross-origin
Policy: same-origin
Policy: strict-origin
Policy: strict-origin-when-cross-origin
Policy: unsafe-url"secureSSLRedirects" : { "enabled" : true },"xssProtection" : {
"enabled" : true,
"mode" : "block"
}By default, annotation security is enabled. This will inspect ALL incoming event executions for the security annotation secured. If you do not want to use annotation security, we recommend you turn it off to avoid the inspection of events.
This global validator will be used to validate authentication/authorization. The default is CBAuthValidator@cbsecurity. This object needs to match the interface: cbsecurity.interfaces.ISecurityValidator . The available validators we ship are:
CBAuth Validator: this is the default validator, which uses the cbauth module. It provides authentication and permission-based security.
CFML Security Validator: Coldbox security has had this validator since version 1, and it will talk to the ColdFusion engine's security methods (cflogin,cflogout). It provides authentication and role-based security.
Basic Auth Validator: This validator secures your app via basic authentication browser challenges to incoming requests. It can also work with the BasicAuthUserService and provide you a basic user credentials storage within your configuration file.
JWT Validator: If you want to use JSON Web Tokens, the JWT Validator provides authorization and authentication by validating incoming access/refresh tokens via headers for RESTFul API communications.
Custom Validator: You can define your own authentication and authorization engines and plug them into the cbsecurity framework.
This setting is used to set the global event that will be executed or redirected to if an invalid authentication is detected. Usually, you want to direct users to a login screen.
We set the default event above, but how do we get there? This setting is the action the firewall will take when an invalid authentication event is detected.
redirect - Redirect them to the invalidAuthenticationEvent
override - Override the incoming event to the invalidAuthenticationEvent
block - Block the request entirely with a 401 Not Authorized response.
This setting is used to set the global event that will be executed or redirected to if an invalid authorization is detected. Usually, you could direct them to a not authorized page.
We set the default event above, but how do we get there? This setting is the action the firewall will take when an invalid authorization event is detected.
redirect - Redirect them to the invalidAuthorizationEvent
override - Override the incoming event to the invalidAuthorizationEvent
block - Block the request entirely with a 401 Not Authorized response.
You can enable the firewall logs, and CBSecurity will log all blocks the firewall detects. By default, it is disabled, but if you enable the logs, we will create the table for you.
We have also included a migrations file so you can add this to your database migrations schemas. Just run: migrate create create_cbsecurity_logs_table and fill it out with this:
This key defines where rules come from and how they interact with the firewall. The rules key can be of two types:
An array of rules
A struct of configuration with a rule source
This is the shorthand way of defining global rules.
If this setting is a struct, you can configure how the rules behave and where they come from JSON, XML, database, model, etc.
This is true by default. It tells the firewall to use regular expression matching against white and secure lists in a rule.
If true then if a security rule needs to do a redirect, it will force the redirect to SSL. This defaults to false.
This is a collection of name-value pairs that each security rule will inherit by default.
This is an array that holds all the rules you can define in CFML. This is the same as making the entire rules key an array of rules.
The provider key is how you can define rules from the following sources:
A JSON file
An XML file
From a model object via a method call
From a database
Here are the different ways you can define rules from other sources rather than inline:
Please note that defining rules are both the same in a global ColdBox config as in a ModuleConfig.
/**
* --------------------------------------------------------------------------
* Firewall Settings
* --------------------------------------------------------------------------
* The firewall is used to block/check access on incoming requests via security rules or via annotation on handler actions.
* Here you can configure the operation of the firewall and especially what Validator will be in charge of verifying authentication/authorization
* during a matched request.
*/
firewall : {
// Auto load the global security firewall automatically, else you can load it a-la-carte via the `Security` interceptor
"autoLoadFirewall" : true,
// The Global validator is an object that will validate the firewall rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "CBAuthValidator@cbsecurity",
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : false,
"dsn" : "",
"schema" : "",
"table" : "cbsecurity_logs",
"autoCreate" : true
}
// Firewall Rules, this can be a struct of detailed configuration
// or a simple array of inline rules
"rules" : {
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// A collection of default name-value pairs to add to ALL rules
// This way you can add global roles, permissions, redirects, etc
"defaults" : {},
// You can store all your rules in this inline array
"inline" : [],
// If you don't store the rules inline, then you can use a provider to load the rules
// The source can be a json file, an xml file, model, db
// Each provider can have it's appropriate properties as well. Please see the documentation for each provider.
"provider" : { "source" : "", "properties" : {} }
}
},autoLoadFirewall : falsehandlerAnnotationSecurity : falsevalidator : "BasicAuthValidator@cbsecurity""invalidAuthenticationEvent" : "security.login","invalidAuthorizationEvent" : "dashboard.notAuthorized","logs" : {
"enabled" : false,
"dsn" : "",
"schema" : "",
"table" : "cbsecurity_logs",
"autoCreate" : true
}component {
variables.INDEX_COLUMNS = [
"userId",
"userAgent",
"ip",
"host",
"httpMethod",
"path",
"referer"
];
function up( schema, qb ){
schema.create( "cbsecurity_logs", function( table ){
table.string( "id", 36 ).primaryKey();
table.timestamp( "logDate" ).withCurrent();
table.string( "action" );
table.string( "blockType" );
table.string( "ip" );
table.string( "host" );
table.string( "httpMethod" );
table.string( "path" );
table.string( "queryString" );
table.string( "referer" ).nullable();
table.string( "userAgent" );
table.string( "userId" ).nullable();
table.longText( "securityRule" ).nullable();
table.index( [ "logDate", "action", "blockType" ], "idx_cbsecurity" );
INDEX_COLUMNS.each( ( key ) => {
table.index( [ arguments.key ], "idx_cbsecurity_#arguments.key#" );
} );
} );
}
function down( schema, qb ){
schema.drop( "cbsecurity_logs" );
}
}"rules" : [
// should use direct action and do a global redirect
{
"whitelist" : "",
"securelist" : "admin",
"match" : "event",
"roles" : "admin",
"permissions" : "",
"action" : "redirect",
"httpMethods" : "*"
},
// Match only put/post
{
"whitelist" : "",
"securelist" : "putpost",
"match" : "event",
"roles" : "",
"permissions" : "",
"action" : "block",
"httpMethods" : "put,post"
},
{
"whitelist" : "",
"securelist" : "cfide",
"match" : "url",
"roles" : "",
"permissions" : "",
"action" : "redirect",
"allowedIPs" : "10.0.0.1"
},
// no action, use global default action
{
"whitelist" : "",
"securelist" : "noAction",
"match" : "url",
"roles" : "admin",
"permissions" : "",
"httpMethods" : "*"
}
]"rules" : {
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// A collection of default name-value pairs to add to ALL rules
// This way you can add global roles, permissions, redirects, etc
"defaults" : {},
// You can store all your rules in this inline array
"inline" : [],
// If you don't store the rules inline, then you can use a provider to load the rules
// The source can be a json file, an xml file, model, db
// Each provider can have it's appropriate properties as well. Please see the documentation for each provider.
"provider" : { "source" : "", "properties" : {} }
}defaults : {
action : "block",
roles : "users"
}"rules" : {
// You can store all your rules in this inline array
"inline" : [
// should use direct action and do a global redirect
{
"whitelist" : "",
"securelist" : "admin",
"match" : "event",
"roles" : "admin",
"permissions" : "",
"action" : "redirect",
"httpMethods" : "*"
},
// Match only put/post
{
"whitelist" : "",
"securelist" : "putpost",
"match" : "event",
"roles" : "",
"permissions" : "",
"action" : "block",
"httpMethods" : "put,post"
}
]
}
CBSecurity also provides you with a JWT (Json Web Tokens) authentication and authorization system.
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.
You can find much more information about JWT at jwt.io.
JSON Web Tokens have become the standard for authenticating and authorizing API requests. They can be used on their own or with an oauth/single sign-on server as well.
Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.
Information Exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with.
The ColdBox Security module will assist you with all the generation, decoding, encoding and security aspects of JWT. All you need to do is, configure it, create a few standard files and off you go.
The tokens created by the JWT services will have the mandatory headers, but also will have a standardizes payload structure. This payload structure can also be customized as you see fit.
A JSON Web Token encodes a series of claims in a JSON object. Some of these claims have specific meanings, while others are left to be interpreted by the users. You can consider claims to be the keys of the payload structure, and it can contain pretty much anything you like.
Here are the base claims that the ColdBox Security JWT token creates for you automatically:
Issuer (iss) - The issuer of the token (defaults to the application's base URL)
Issued At (iat) - When the token was issued (Unix timestamp)
Subject (sub) - This holds the identifier for the token (defaults to user id)
You can add much more to this payload via the JWT service methods or the User that models the token.
The service can be found here cbsecurity.models.jwt.JWTService and can be retrieved by either injecting the service (JwtService@cbsecurity) or using our helper method (jwtAuth()).
To begin exploring the JWT capabilities, let's first examine how to configure it.
You can check out our for a more in-depth tutorial. Here are the basic settings you would put in the cbsecurity configuration:
The next step is ensuring that our JWT services can handle the construction of the JWT tokens as per YOUR requirements. So your User object must implement our JWTSubject interface with the following functions:
Basically, it's two functions:
getJwtCustomClaims( payload ) - This is a struct of custom claims to incorporate into the token payload at construction time. This can be ANYTHING you like.
getJwtScopes() - We will also call this at construction time in order to incorporate the right permission scopes into the token according to your user. This must be an array of scopes/permissions.
Since also the authentication services will be used with JWT, your user object might end up looking like this:
The JWT validators must talk to the authentication and user services. Please refer to the page to configure and create them.
Ok, now we can focus on all the wonderful methods the JWT service offers:
attempt( username, password, [ customClaims:struct ] ):token - Attempt to authenticate a user with the authentication service and return the token using the identifier and custom claims if successful. Exception if invalid authentication
fromUser( user, [ customClaims:struct ] ):token - Generate a token according to the passed user object and custom claims.
encode( struct payload ):token - Generate a raw JWT token from a native payload struct.
verify( required token ):boolean - Verify a token string or throws an exception
decode( required token ):struct - Decode and retrieve the passed-in token to CFML struct
parseToken( token, storeInContext, authenticate ):struct - Get the decoded token using the headers strategy and store it in the prc.jwt_token and the decoded data as prc.jwt_payload if it verifies correctly. Throws: TokenExpiredException if the token is expired, TokenInvalidException if the token doesn't verify decoding, TokenNotFoundException if not found
getToken():string
authenticate( [payload] ):User - Authenticates a passed or detected token payload and return the user it represents
getuser() - Get the authenticated user according to the access token detected
logout() - Logout a user and invalidate their token
invalidateAll( async:false ) - Invalidate all access and refresh tokens in permanent storage
invalidate( token ) - Invalidates the incoming token by removing it from the permanent storage.
isTokenInStorage( token ) - Checks if the passed token exists in permanent storage.
attempt( username, password, [ customClaims:struct ] ):struct - Attempt to authenticate a user with the authentication service and if successful, return a struct containing an access and refresh token.
fromUser( user, [ customClaims:struct ] ):struct - Generate a struct of refresh and access tokens according to the passed user object and custom claims.
That's it; we are ready to put it all together. Now cbsecurity knows about your authentication/user services, can talk to your user to create tokens, and can guard the incoming requests via the JWT Validator. Here is a sample controller for login, logout, and user registration:
Let's configure some routes first:
Then build out the Auth controller
Make sure you add validation!
That's it; we can now login a user, give them a token, register a new user and give them their token, and log them out. The next step is to build your rules and/or security annotations and ensure the is configured for your global app or module.
To implement JWT authentication in your application, you may need to modify some web server settings. Most web servers have default content length restrictions on the size of an individual header. If your web server platform has such a default enabled, you will need to increase the buffer size to accommodate the presence of JTW tokens in both the request and response headers.
The size of a JWT token header, encrypted via the default cbSecurity HMAC512 algorithm, is around 44 kilobytes. As such, you will need to allow for at least that size. Below are some examples of common web server configurations
The following configuration may be applied to the main NGINX http configuration block to allow for the presence of tokens in both the request and response headers:
You will need to modify two registry keys:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters\MaxFieldLength - Sets an upper limit, in bytes, for each header. The default value is 65534 bytes and the maximum value is 65534 bytes ( 64kb )
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters\MaxRequestBytes - Sets the upper limit for the request line and the headers, combined. As such 128K should allow for both long URLs, as well as JWT tokens in the headers. The default value is 16384 bytes and the maximum value is 16777216 bytes ( 16 MB )
You will need to add a LimitRequestFieldSize setting in each <VirtualHost...> entry in order increase the default header size from the default 8 kilobytes. Example, with a setting of 128 kilobytes:
Expiration time (exp) - The token expiry date (Unix timestamp)
Unique ID (jti) - A unique identifier for the token (md5 of the sub and iat claims)
Scopes (scope) - A space-delimited string of scopes attached to the token
Refresh Token (cbsecurity_refresh ) - If you use refresh tokens, this custom claim will be added to the payload.
prc.jwt_tokenparseToken()getPayload():struct - Get the stored token from prc.jwt_payload, if it doesn't exist, it tries to parse it via parseToken(), if no token is set, this will be an empty struct.
setToken( token ):JWTService - Store the token in prc.jwt_token, and store the decoded version in prc.jwt_payload
getTokenStorage( force:false ) - Get the current token storage implementation. You can also force-create it again if needed.
{
"iat": 1569340662,
"scope": "",
"iss": "http://127.0.0.1:56596/",
"sub": 123,
"exp": 1569344262,
"jti": "12954F907C0535ABE97F761829C6BD11"
}{
"iat": 1569340662,
"scope": "",
"iss": "http://127.0.0.1:56596/",
"sub": 2222,
"exp": 1569344262,
"jti": "234234CDDEEDD",
"cbsecurity_refresh" : true
}// Injection
property name="jwtService" inject="JwtService@cbsecurity";
// Helper Method in any handler/layout/interceptors/views
jwtAuth()cbsecurity : {
// The WireBox ID of the authentication service to use in cbSecurity which must adhere to the cbsecurity.interfaces.IAuthService interface.
authenticationService : "authenticationService@cbauth",
// WireBox ID of the user service to use
userService : "",
// The name of the variable to use to store an authenticated user in prc scope if using a validator that supports it.
prcUserVariable : "oCurrentUser",
// JWT Settings
jwt : {
// The issuer authority for the tokens, placed in the `iss` claim
issuer : "",
// The jwt secret encoding key, defaults to getSystemEnv( "JWT_SECRET", "" )
// This key is only effective within the `config/Coldbox.cfc`. Specifying within a module does nothing.
secretKey : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well.
customAuthHeader : "x-auth-token",
// The expiration in minutes for the jwt tokens
expiration : 60,
// If true, enables refresh tokens, token creation methods will return a struct instead
// of just the access token. e.g. { access_token: "", refresh_token : "" }
enableRefreshTokens : false,
// The default expiration for refresh tokens, defaults to 30 days
refreshExpiration : 10080,
// The Custom header to inspect for refresh tokens
customRefreshHeader : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
enableAutoRefreshValidator : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
enableRefreshEndpoint : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
algorithm : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
requiredClaims : [] ,
// The token storage settings
tokenStorage : {
// enable or not, default is true
"enabled" : true
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
cacheName : "default"
}
}
}
}/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* If you use the jwt services, then your jwt subject user must implement this interface
*/
interface{
/**
* A struct of custom claims to add to the JWT token when creating it
*
* @payload The actual payload structure that was used in the request
*
* @return A structure of custom claims
*/
struct function getJwtCustomClaims( required struct payload );
/**
* This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization.
*/
array function getJwtScopes();
}
component accessors="true" {
property name="auth" inject="authenticationService@cbauth";
property name="id";
property name="firstName";
property name="lastName";
property name="username";
property name="password";
function init(){
variables.id = "";
variables.firstName = "";
variables.lastName = "";
variables.username = "";
variables.password = "";
variables.permissions = [ "write", "read" ];
return this;
}
boolean function isLoaded(){
return ( !isNull( variables.id ) && len( variables.id ) );
}
/**
* A struct of custom claims to add to the JWT token
*/
struct function getJWTCustomClaims(){
return { "role" : "admin" };
}
/**
* This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization.
*/
array function getJWTScopes(){
return variables.permissions;
}
/**
* Verify if the user has one or more of the passed in permissions
*
* @permission One or a list of permissions to check for access
*
*/
boolean function hasPermission( required permission ){
if ( isSimpleValue( arguments.permission ) ) {
arguments.permission = listToArray( arguments.permission );
}
return arguments.permission
.filter( function(item){
return ( variables.permissions.ListFindNoCase( item ) );
} )
.len();
}
}{
"access_token" : "AYjcyMzY3ZDhiNmJk",
"refresh_token" : "RjY2NjM5NzA2OWJj"
}post( "/api/login" , "api.auth.login" );
post( "/api/logout" , "api.auth.logout" );
post( "/api/register" , "api.auth.register" );component{
function login( event, rc, prc ){
param rc.username = "";
param rc.password = "";
try {
var token = jwtAuth().attempt( rc.username, rc.password );
return {
"error" : false,
"data" : token,
"message" : "Bearer token created and it expires in #jwtAuth().getSettings().jwt.expiration# minutes"
};
} catch ( "InvalidCredentials" e ) {
event.setHTTPHeader( statusCode = 401, statusText = "Unauthorized" );
return { "error" : true, "data" : "", "message" : "Invalid Credentials" };
}
}
function register( event, rc, prc ){
param rc.firstName = "";
param rc.lastName = "";
param rc.username = "";
param rc.password = "";
prc.oUser = populateModel( "User" );
userService.create( prc.oUser );
var token = jwtAuth().fromuser( prc.oUser );
return {
"error" : false,
"data" : token,
"message" : "User registered correctly and Bearer token created and it expires in #jwtAuth().getSettings().jwt.expiration# minutes"
};
}
function logout( event, rc, prc ){
jwtAuth().logout();
return { "error" : false, "data" : "", "message" : "Successfully logged out" };
}
}http {
# These settings affect outbound headers via proxy server
proxy_buffer_size 64k;
proxy_buffers 4 128k;
proxy_busy_buffers_size 128k;
# These settings affect the http client request headers
client_body_buffer_size 128k;
client_header_buffer_size 64k;
large_client_header_buffers 8 128k;
# These settings affect HTTP/2 headers, however some versions of NGINX will throw an error on HTTP/1 requests if these are not present
http2_max_header_size 128k;
http2_max_field_size 1000m;
}<VirtualHost 10.10.50.50:80>
ServerName www.mysite.com
LimitRequestFieldSize 128000
RewriteEngine On
...
...
</VirtualHost>ColdBox security can work with ANY authentication service provider.
CBSecurity has been designed to work with ANY authentication and user service provider. CBSecurity is in charge of intercepting requests and delegating access verification to Security Validators, leveraging Authentication and User Services to allow access to a resource or block the request ultimately.
We have created an interface that must be implemented by any service that is going to be used with CBSecurity: cbsecurity.interfaces.IAuthService. Then you would configure this service in the Configuration File alongside the validator you would like to use (cbauth, JWT, basic auth, etc.)
By default, CBSecurity ships with a very simple yet powerful authentication service and validator called cbauth. This module gives you the ability to login, logout, verify and tracker users across requests using session and request storages. All you have to do is provide a User Service class that will connect to your storage of choice in order to operate. Here is a typical cbauth configuration that will exist alongside the cbsecurity module settings:
This user service must also adhere to our User Service interface: cbsecurity.interfaces.IUserService and the user objects it must produce also will need to adhere to our user interface: cbsecurity.interface.IAuthUser.
If you are using cbauth, please keep in mind that it stores only the user id in session. All other AuthUser properites are transient as they are part of the request scope.
This interface has been provided by convenience, and it is not mandatory at runtime since cbauth implements it: (cbsecurity.interfaces.IAuthService)
You can find the information for cbauth in its book:
As you can see from above, the authentication services all expect a User object to model your user in the system. So your User object must also adhere to the following methods modeled by the cbsecurity.interfaces.IAuthUser interface. This will allow the validators and JWT services to get the appropriate data.
If you are using cbauth or any of our JWT features, then we will also require you to register a user service class that can provide us with the correct data to encapsulate security using the userService setting. We have provided this interface for your usage:
Ok, now that we have discovered the basics of CBSecurity, why don't we build a simple example using a database-driven approach to security with cbauth. Please note that we also have a approach as well.
We will use the defaults CBSecurity ships with to protect our new admin console with cbauth.
As you can see, we don't have to specify an authentication provider or validator; it's already defaulted to cbauth. I only have to specify the user service that will provide the User object and user information from my database.
Ok, before I go into building my user service, I would have to create a User object that the service would return so cbauth can use it. However, CBSecurity already ships with a basic authentication user object I can use: cbsecurity.models.basicauth.BasicAuthUser.
I will model my database table after it and create the following columns:
id
firstName
lastName
Ok, now let's build our basic service that will leverage the DB and simple password hashing. Remember, this object must implement cbsecurity.interfaces.IUserService :
At this point, I have satisfied all the requirements for CBSecurity to work:
Create a user service that knows how to get users by id, username, and credentials
Created my database table and connection
Now I have to build out:
Login screen
Login processor
Logout processor
/**
* --------------------------------------------------------------------------
* Authentication Services
* --------------------------------------------------------------------------
* Here you will configure which service is in charge of providing authentication for your application.
* By default we leverage the cbauth module which expects you to connect it to a database via your own User Service.
*
* Available authentication providers:
* - cbauth : Leverages your own UserService that determines authentication and user retrieval
* - basicAuth : Leverages basic authentication and basic in-memory user registration in our configuration
* - custom : Any other service that adheres to our IAuthService interface
*/
authentication : {
// The WireBox ID of the authentication service to use which must adhere to the cbsecurity.interfaces.IAuthService interface.
"provider" : "authenticationService@cbauth",
// WireBox ID of the user service to use when leveraging user authentication, we default this to whatever is set
// by cbauth or basic authentication. (Optional)
"userService" : cbauth.userServiceclass,
// The name of the variable to use to store an authenticated user in prc scope on all incoming authenticated requests
"prcUserVariable" : "oCurrentUser"
},
firewall : {
"validator" : "CBAuthValidator@cbsecurity"
}usernamepassword
permissions
roles

cbauth = {
// This is the path to your user object that contains the credential validation methods
userServiceClass = "MyUserService"
},/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* If you register an authentication service with cbsecurity it must adhere to this interface
*/
interface{
/**
* Get the authenticated user
*
* @throws NoUserLoggedIn : If the user is not logged in
*
* @return User that implements IAuthUser
*/
any function getUser();
/**
* Verifies if a user is logged in
*/
boolean function isLoggedIn();
/**
* Try to authenticate a user into the system. If the authentication fails an exception is thrown, else the logged in user object is returned
*
* @username The username to log in with
* @password The password to log in with
*
* @throws InvalidCredentials
*
* @return User : The logged in user object
*/
any function authenticate( required username, required password );
/**
* Login a user into our persistent scopes
*
* @user The user object to log in
*
* @return The same user object so you can do functional goodness
*/
function login( required user );
/**
* Logs out the currently logged in user from the system
*/
function logout();
}/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* If you use a user with a user service or authentication service, it must implement this interface
*/
interface{
/**
* Return the unique identifier for the user
*/
function getId();
/**
* Verify if the user has one or more of the passed in permissions
*
* @permission One or a list of permissions to check for access
*
*/
boolean function hasPermission( required permission );
/**
* Verify if the user has one or more of the passed in roles
*
* @role One or a list of roles to check for access
*
*/
boolean function hasRole( required role );
}interface{
/**
* Verify if the incoming username/password are valid credentials.
*
* @username The username
* @password The password
*/
boolean function isValidCredentials( required username, required password );
/**
* Retrieve a user by username
*
* @return User that implements JWTSubject and/or IAuthUser
*/
function retrieveUserByUsername( required username );
/**
* Retrieve a user by unique identifier
*
* @id The unique identifier
*
* @return User that implements JWTSubject and/or IAuthUser
*/
function retrieveUserById( required id );
}moduleSettings : {
cbauth : {
// Our service class
userServiceClass : "UserService"
},
cbsecurity : {
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "security.login",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "security.notAuthorized",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : true,
"table" : "cbsecurity_logs"
},
rules : [
{
secureList : "^admin"
}
]
}
};/**
* This is a basic user object that can be used with cbsecurity.
* It implements the following interfaces
* - cbsecurity.interfaces.jwt.IJwtSubject
* - cbsecurity.interfaces.IAuthUser
*/
component accessors="true" {
property name="id";
property name="firstName";
property name="lastName";
property name="username";
property name="password";
property name="permissions";
property name="roles";
function init(){
variables.id = "";
variables.firstName = "";
variables.lastName = "";
variables.username = "";
variables.password = "";
variables.permissions = [];
variables.roles = [];
return this;
}
function setRoles( roles ){
if ( isSimpleValue( arguments.roles ) ) {
arguments.roles = listToArray( arguments.roles );
}
variables.roles = arguments.roles;
return this;
}
function setPermissions( permissions ){
if ( isSimpleValue( arguments.permissions ) ) {
arguments.permissions = listToArray( arguments.permissions );
}
variables.permissions = arguments.permissions;
return this;
}
/**
* Verify if this is a valid user or not
*/
boolean function isLoaded(){
return ( !isNull( variables.id ) && len( variables.id ) );
}
/**
* A struct of custom claims to add to the JWT token
*/
struct function getJWTCustomClaims( required struct payload ){
return { "role" : variables.roles.toList() };
}
/**
* This function returns an array of all the scopes that should be attached to the JWT token that will be used for authorization.
*/
array function getJWTScopes(){
return variables.permissions;
}
/**
* Verify if the user has one or more of the passed in permissions
*
* @permission One or a list of permissions to check for access
*/
boolean function hasPermission( required permission ){
if ( isSimpleValue( arguments.permission ) ) {
arguments.permission = listToArray( arguments.permission );
}
return arguments.permission
.filter( function( item ){
return ( variables.permissions.findNoCase( item ) );
} )
.len();
}
/**
* Verify if the user has one or more of the passed in roles
*
* @role One or a list of roles to check for access
*/
boolean function hasRole( required role ){
if ( isSimpleValue( arguments.role ) ) {
arguments.role = listToArray( arguments.role );
}
return arguments.role
.filter( function( item ){
return ( variables.roles.findNoCase( item ) );
} )
.len();
}
}
component accessors="true" singleton {
/*********************************************************************************************/
/** DI **/
/*********************************************************************************************/
property name="populator" inject="wirebox:populator";
property name="wirebox" inject="wirebox";
/*********************************************************************************************/
/** Static Settings **/
/*********************************************************************************************/
static {
hashAlgorithm = "SHA-512";
hashIterations = 5;
}
/**
* Constructor
*/
function init(){
return this;
}
/**
* Hash the incoming target according to our hashing algorithm and settings
* @target The string target to hash
*/
private string function hashSecurely( required string target ){
return hash( arguments.target, static.hashAlgorithm, "UTF-8", static.hashIterations );
}
/**
* New User Dispenser
*/
BasicAuthUser function new() provider="BasicAuthUser@cbsecurity"{
}
/**
* Get a new user by id
*
* @id The id to get the user with
*
* @return The located user or a new un-loaded user object
*/
BasicAuthUser function retrieveUserById( required id ){
return queryExecute(
"select * from users where id = :id",
{ id : arguments.id },
{ returnType : "array" }
).reduce( ( result, data ) =>{
return populator.populateFromStruct( new(), arguments.data );
}, new() );
}
/**
* Get a user by username
*
* @username The username to get the user with
*
* @return The valid user object representing the username or an empty user object
*/
BasicAuthUser function retrieveUserByUsername( required username ){
return queryExecute(
"select * from users where username = :username",
{ username : arguments.username },
{ returnType : "array" }
).reduce( ( result, data ) =>{
return populator.populateFromStruct( new(), arguments.data );
}, new() );
}
/**
* Verify if the incoming username and password are valid credentials in this user storage
*
* @username The username to test
* @password The password to test
*
* @return true if valid, else false
*/
boolean function isValidCredentials( required username, required password ){
var oUser = retrieveUserByUsername( arguments.username );
if ( !oUser.isLoaded() ) {
return false;
}
return hashSecurely( arguments.password ) eq oUser.getPassword();
}
}
component{
function login( event, rc, prc ){
event.setView( "security.login" );
}
}<cfoutput>
<h1>Security Login</h1>
<cfif flash.exists( "message" )>
<div style="border: 1px solid gray; background-color: ##f29595; margin: 20px 0px; padding: 10px">
#flash.get( "message" )#
</div>
</cfif>
#html.startForm( action="security.doLogin" )#
#html.textField( name="username", placeholder="username" )#
<br>
#html.passwordField( name="password", placeholder="password" )#
<br>
#html.submitButton( name="Submit" )#
#html.endForm()#
</cfoutput>component{
function doLogin( event, rc, prc ){
try {
var oUser = cbsecure().authenticate( rc.username ?: "", rc.password ?: "" );
return "You are logged in!";
} catch ( "InvalidCredentials" e ) {
flash.put( "message", "Invalid credentials, try again!" );
relocate( "security/login" );
}
}
function doLogout( event, rc, prc ){
cbsecure().logout();
flash.put( "message", "Bye bye!" );
relocate( "security/login" );
}
}


How to configure CBSecurity
By default, the security module will register itself for you using the module configuration settings you define in theconfig/ColdBox.cfc. You can create a cbsecurity key in the modulesettings or if you are in ColdBox 7 you can create a config/modules/cbsecurity.cfc as well.
Here you can see how to configure CBSecurity, but you can also navigate to the different configuration sections for an in-depth overview of those settings:
In ColdBox 7 you can segregate module configurations so they are more manageable and have their own identity. Just create a config/modules/cbsecurity.cfc with a nice configure() method:
Each module can also have its own CBSecurity settings which override or collaborate with the global settings. So what can a module do:
Have its own validator
Have its own security rules
Have its own invalid authentication event and action
Have its own invalid authorization event and action
You will create a cbsecurity struct within the module's settings struct in the ModuleConfig.cfc
Please note that a module's security rules will be PREPENDED to the global rules
Also note that if modules are loaded dynamically, it will still inspect them and register them if cbsecurity settings are found. The same goes for unloading, the entire security rules for that module will cease to exist.
// Module Settings
moduleSettings = {
cbauth = {
// This is the path to your user object that contains the credential validation methods
userServiceClass = ""
},
cbsecurity = {
/**
* --------------------------------------------------------------------------
* Authentication Services
* --------------------------------------------------------------------------
* Here you will configure which service is in charge of providing authentication for your application.
* By default we leverage the cbauth module which expects you to connect it to a database via your own User Service.
*
* Available authentication providers:
* - cbauth : Leverages your own UserService that determines authentication and user retrieval
* - basicAuth : Leverages basic authentication and basic in-memory user registration in our configuration
* - custom : Any other service that adheres to our IAuthService interface
*/
authentication : {
// The WireBox ID of the authentication service to use which must adhere to the cbsecurity.interfaces.IAuthService interface.
"provider" : "authenticationService@cbauth",
// WireBox ID of the user service to use when leveraging user authentication, we default this to whatever is set
// by cbauth or basic authentication. (Optional)
"userService" : "",
// The name of the variable to use to store an authenticated user in prc scope on all incoming authenticated requests
"prcUserVariable" : "oCurrentUser"
},
/**
* --------------------------------------------------------------------------
* Basic Auth
* --------------------------------------------------------------------------
* These settings are used so you can configure the hashing patterns of the user storage
* included with cbsecurity. These are only used if you are using the `BasicAuthUserService` as
* your service of choice alongside the `BasicAuthValidator`
*/
basicAuth : {
// Hashing algorithm to use
hashAlgorithm : "SHA-512",
// Iterates the number of times the hash is computed to create a more computationally intensive hash.
hashIterations : 5,
// User storage: The `key` is the username. The value is the user credentials that can include
// { roles: "", permissions : "", firstName : "", lastName : "", password : "" }
users : {}
},
/**
* --------------------------------------------------------------------------
* CSRF - Cross Site Request Forgery Settings
* --------------------------------------------------------------------------
* These settings configures the cbcsrf module. Look at the module configuration for more information
*/
csrf : {
// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
enableAutoVerifier : false,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [],
// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
// If you do not want expiring tokens, then set this value to 0
rotationTimeout : 30,
// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
enableEndpoint : false,
// The WireBox mapping to use for the CacheStorage
cacheStorage : "CacheStorage@cbstorages",
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : true
},
/**
* --------------------------------------------------------------------------
* Firewall Settings
* --------------------------------------------------------------------------
* The firewall is used to block/check access on incoming requests via security rules or via annotation on handler actions.
* Here you can configure the operation of the firewall and especially what Validator will be in charge of verifying authentication/authorization
* during a matched request.
*/
firewall : {
// Auto load the global security firewall automatically, else you can load it a-la-carte via the `Security` interceptor
"autoLoadFirewall" : true,
// The Global validator is an object that will validate the firewall rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "CBAuthValidator@cbsecurity",
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : false,
"dsn" : "",
"schema" : "",
"table" : "cbsecurity_logs",
"autoCreate" : true
},
// Firewall Rules, this can be a struct of detailed configuration
// or a simple array of inline rules
"rules" : {
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// A collection of default name-value pairs to add to ALL rules
// This way you can add global roles, permissions, redirects, etc
"defaults" : {},
// You can store all your rules in this inline array
"inline" : [],
// If you don't store the rules inline, then you can use a provider to load the rules
// The source can be a json file, an xml file, model, db
// Each provider can have it's appropriate properties as well. Please see the documentation for each provider.
"provider" : { "source" : "", "properties" : {} }
}
},
/**
* --------------------------------------------------------------------------
* Security Visualizer
* --------------------------------------------------------------------------
* This is a debugging panel that when active, a developer can visualize security settings and more.
* You can use the `securityRule` to define what rule you want to use to secure the visualizer but make sure the `secured` flag is turned to true.
* You don't have to specify the `secureList` key, we will do that for you.
*/
visualizer : {
"enabled" : false,
"secured" : false,
"securityRule" : {}
},
/**
* --------------------------------------------------------------------------
* Security Headers
* --------------------------------------------------------------------------
* This section is the way to configure cbsecurity for header detection, inspection and setting for common
* security exploits like XSS, ClickJacking, Host Spoofing, IP Spoofing, Non SSL usage, HSTS and much more.
*/
securityHeaders : {
// Master switch for security headers
"enabled" : true,
// If you trust the upstream then we will check the upstream first for specific headers
"trustUpstream" : false,
// Content Security Policy
// Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks,
// including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to
// site defacement, to malware distribution.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
"contentSecurityPolicy" : {
// Disabled by defautl as it is totally customizable
"enabled" : false,
// The custom policy to use, by default we don't include any
"policy" : ""
},
// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in
// the Content-Type headers should be followed and not be changed => X-Content-Type-Options: nosniff
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
"contentTypeOptions" : { "enabled" : true },
"customHeaders" : {
// Name : value pairs as you see fit.
},
// Disable Click jacking: X-Frame-Options: DENY OR SAMEORIGIN
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
"frameOptions" : { "enabled" : true, "value" : "SAMEORIGIN" },
// HTTP Strict Transport Security (HSTS)
// The HTTP Strict-Transport-Security response header (often abbreviated as HSTS)
// informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it
// using HTTP should automatically be converted to HTTPS.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security,
"hsts" : {
"enabled" : true,
// The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS, 1 year is the default
"max-age" : "31536000",
// See Preloading Strict Transport Security for details. Not part of the specification.
"preload" : false,
// If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
"includeSubDomains" : false
},
// Validates the host or x-forwarded-host to an allowed list of valid hosts
"hostHeaderValidation" : {
"enabled" : false,
// Allowed hosts list
"allowedHosts" : ""
},
// Validates the ip address of the incoming request
"ipValidation" : {
"enabled" : false,
// Allowed IP list
"allowedIPs" : ""
},
// The Referrer-Policy HTTP header controls how much referrer information (sent with the Referer header) should be included with requests.
// Aside from the HTTP header, you can set this policy in HTML.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
"referrerPolicy" : { "enabled" : true, "policy" : "same-origin" },
// Detect if the incoming requests are NON-SSL and if enabled, redirect with SSL
"secureSSLRedirects" : { "enabled" : false },
// Some browsers have built in support for filtering out reflected XSS attacks. Not foolproof, but it assists in XSS protection.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection,
// X-XSS-Protection: 1; mode=block
"xssProtection" : { "enabled" : true, "mode" : "block" }
},
/**
* --------------------------------------------------------------------------
* Json Web Tokens Settings
* --------------------------------------------------------------------------
* Here you can configure the JWT services for operation and storage. In order for your firewall
* to leverage JWT authentication/authorization you must make sure you use the `JwtAuthValidator` as your
* validator of choice; either globally or at the module level.
*/
jwt : {
// The issuer authority for the tokens, placed in the `iss` claim
"issuer" : "",
// The jwt secret encoding key, defaults to getSystemEnv( "JWT_SECRET", "" )
"secretKey" : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well.
"customAuthHeader" : "x-auth-token",
// The expiration in minutes for the jwt tokens
"expiration" : 60,
// If true, enables refresh tokens, longer lived tokens (not implemented yet)
"enableRefreshTokens" : false,
// The default expiration for refresh tokens, defaults to 30 days
"refreshExpiration" : 10080,
// The Custom header to inspect for refresh tokens
"customRefreshHeader" : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
"enableAutoRefreshValidator" : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
"enableRefreshEndpoint" : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
"algorithm" : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
"requiredClaims" : [] ,
// The token storage settings
"tokenStorage" : {
// enable or not, default is true
"enabled" : true,
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
"cacheName" : "default"
}
}
}
};component{
function configure(){
return {
/**
* --------------------------------------------------------------------------
* Authentication Services
* --------------------------------------------------------------------------
* Here you will configure which service is in charge of providing authentication for your application.
* By default we leverage the cbauth module which expects you to connect it to a database via your own User Service.
*
* Available authentication providers:
* - cbauth : Leverages your own UserService that determines authentication and user retrieval
* - basicAuth : Leverages basic authentication and basic in-memory user registration in our configuration
* - custom : Any other service that adheres to our IAuthService interface
*/
authentication : {
// The WireBox ID of the authentication service to use which must adhere to the cbsecurity.interfaces.IAuthService interface.
"provider" : "authenticationService@cbauth",
// WireBox ID of the user service to use when leveraging user authentication, we default this to whatever is set
// by cbauth or basic authentication. (Optional)
"userService" : "",
// The name of the variable to use to store an authenticated user in prc scope on all incoming authenticated requests
"prcUserVariable" : "oCurrentUser"
},
/**
* --------------------------------------------------------------------------
* Basic Auth
* --------------------------------------------------------------------------
* These settings are used so you can configure the hashing patterns of the user storage
* included with cbsecurity. These are only used if you are using the `BasicAuthUserService` as
* your service of choice alongside the `BasicAuthValidator`
*/
basicAuth : {
// Hashing algorithm to use
hashAlgorithm : "SHA-512",
// Iterates the number of times the hash is computed to create a more computationally intensive hash.
hashIterations : 5,
// User storage: The `key` is the username. The value is the user credentials that can include
// { roles: "", permissions : "", firstName : "", lastName : "", password : "" }
users : {}
},
/**
* --------------------------------------------------------------------------
* CSRF - Cross Site Request Forgery Settings
* --------------------------------------------------------------------------
* These settings configures the cbcsrf module. Look at the module configuration for more information
*/
csrf : {
// By default we load up an interceptor that verifies all non-GET incoming requests against the token validations
enableAutoVerifier : false,
// A list of events to exclude from csrf verification, regex allowed: e.g. stripe\..*
verifyExcludes : [],
// By default, all csrf tokens have a life-span of 30 minutes. After 30 minutes, they expire and we aut-generate new ones.
// If you do not want expiring tokens, then set this value to 0
rotationTimeout : 30,
// Enable the /cbcsrf/generate endpoint to generate cbcsrf tokens for secured users.
enableEndpoint : false,
// The WireBox mapping to use for the CacheStorage
cacheStorage : "CacheStorage@cbstorages",
// Enable/Disable the cbAuth login/logout listener in order to rotate keys
enableAuthTokenRotator : true
},
/**
* --------------------------------------------------------------------------
* Firewall Settings
* --------------------------------------------------------------------------
* The firewall is used to block/check access on incoming requests via security rules or via annotation on handler actions.
* Here you can configure the operation of the firewall and especially what Validator will be in charge of verifying authentication/authorization
* during a matched request.
*/
firewall : {
// Auto load the global security firewall automatically, else you can load it a-la-carte via the `Security` interceptor
"autoLoadFirewall" : true,
// The Global validator is an object that will validate the firewall rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "CBAuthValidator@cbsecurity",
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : false,
"dsn" : "",
"schema" : "",
"table" : "cbsecurity_logs",
"autoCreate" : true
},
// Firewall Rules, this can be a struct of detailed configuration
// or a simple array of inline rules
"rules" : {
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// A collection of default name-value pairs to add to ALL rules
// This way you can add global roles, permissions, redirects, etc
"defaults" : {},
// You can store all your rules in this inline array
"inline" : [],
// If you don't store the rules inline, then you can use a provider to load the rules
// The source can be a json file, an xml file, model, db
// Each provider can have it's appropriate properties as well. Please see the documentation for each provider.
"provider" : { "source" : "", "properties" : {} }
}
},
/**
* --------------------------------------------------------------------------
* Security Visualizer
* --------------------------------------------------------------------------
* This is a debugging panel that when active, a developer can visualize security settings and more.
* You can use the `securityRule` to define what rule you want to use to secure the visualizer but make sure the `secured` flag is turned to true.
* You don't have to specify the `secureList` key, we will do that for you.
*/
visualizer : {
"enabled" : false,
"secured" : false,
"securityRule" : {}
},
/**
* --------------------------------------------------------------------------
* Security Headers
* --------------------------------------------------------------------------
* This section is the way to configure cbsecurity for header detection, inspection and setting for common
* security exploits like XSS, ClickJacking, Host Spoofing, IP Spoofing, Non SSL usage, HSTS and much more.
*/
securityHeaders : {
// Master switch for security headers
"enabled" : true,
// If you trust the upstream then we will check the upstream first for specific headers
"trustUpstream" : false,
// Content Security Policy
// Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks,
// including Cross-Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft, to
// site defacement, to malware distribution.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
"contentSecurityPolicy" : {
// Disabled by defautl as it is totally customizable
"enabled" : false,
// The custom policy to use, by default we don't include any
"policy" : ""
},
// The X-Content-Type-Options response HTTP header is a marker used by the server to indicate that the MIME types advertised in
// the Content-Type headers should be followed and not be changed => X-Content-Type-Options: nosniff
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
"contentTypeOptions" : { "enabled" : true },
"customHeaders" : {
// Name : value pairs as you see fit.
},
// Disable Click jacking: X-Frame-Options: DENY OR SAMEORIGIN
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
"frameOptions" : { "enabled" : true, "value" : "SAMEORIGIN" },
// HTTP Strict Transport Security (HSTS)
// The HTTP Strict-Transport-Security response header (often abbreviated as HSTS)
// informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it
// using HTTP should automatically be converted to HTTPS.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security,
"hsts" : {
"enabled" : true,
// The time, in seconds, that the browser should remember that a site is only to be accessed using HTTPS, 1 year is the default
"max-age" : "31536000",
// See Preloading Strict Transport Security for details. Not part of the specification.
"preload" : false,
// If this optional parameter is specified, this rule applies to all of the site's subdomains as well.
"includeSubDomains" : false
},
// Validates the host or x-forwarded-host to an allowed list of valid hosts
"hostHeaderValidation" : {
"enabled" : false,
// Allowed hosts list
"allowedHosts" : ""
},
// Validates the ip address of the incoming request
"ipValidation" : {
"enabled" : false,
// Allowed IP list
"allowedIPs" : ""
},
// The Referrer-Policy HTTP header controls how much referrer information (sent with the Referer header) should be included with requests.
// Aside from the HTTP header, you can set this policy in HTML.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
"referrerPolicy" : { "enabled" : true, "policy" : "same-origin" },
// Detect if the incoming requests are NON-SSL and if enabled, redirect with SSL
"secureSSLRedirects" : { "enabled" : false },
// Some browsers have built in support for filtering out reflected XSS attacks. Not foolproof, but it assists in XSS protection.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection,
// X-XSS-Protection: 1; mode=block
"xssProtection" : { "enabled" : true, "mode" : "block" }
},
/**
* --------------------------------------------------------------------------
* Json Web Tokens Settings
* --------------------------------------------------------------------------
* Here you can configure the JWT services for operation and storage. In order for your firewall
* to leverage JWT authentication/authorization you must make sure you use the `JwtAuthValidator` as your
* validator of choice; either globally or at the module level.
*/
jwt : {
// The issuer authority for the tokens, placed in the `iss` claim
"issuer" : "",
// The jwt secret encoding key, defaults to getSystemEnv( "JWT_SECRET", "" )
"secretKey" : getSystemSetting( "JWT_SECRET", "" ),
// by default it uses the authorization bearer header, but you can also pass a custom one as well.
"customAuthHeader" : "x-auth-token",
// The expiration in minutes for the jwt tokens
"expiration" : 60,
// If true, enables refresh tokens, longer lived tokens (not implemented yet)
"enableRefreshTokens" : false,
// The default expiration for refresh tokens, defaults to 30 days
"refreshExpiration" : 10080,
// The Custom header to inspect for refresh tokens
"customRefreshHeader" : "x-refresh-token",
// If enabled, the JWT validator will inspect the request for refresh tokens and expired access tokens
// It will then automatically refresh them for you and return them back as
// response headers in the same request according to the customRefreshHeader and customAuthHeader
"enableAutoRefreshValidator" : false,
// Enable the POST > /cbsecurity/refreshtoken API endpoint
"enableRefreshEndpoint" : true,
// encryption algorithm to use, valid algorithms are: HS256, HS384, and HS512
"algorithm" : "HS512",
// Which claims neds to be present on the jwt token or `TokenInvalidException` upon verification and decoding
"requiredClaims" : [] ,
// The token storage settings
"tokenStorage" : {
// enable or not, default is true
"enabled" : true,
// A cache key prefix to use when storing the tokens
"keyPrefix" : "cbjwt_",
// The driver to use: db, cachebox or a WireBox ID
"driver" : "cachebox",
// Driver specific properties
"properties" : {
"cacheName" : "default"
}
}
};
}
}settings = {
// CB Security Module Settings
cbsecurity : {
firewall : {
// Module Relocation when an invalid access is detected, instead of each rule declaring one.
"invalidAuthenticationEvent" : "api:Home.onInvalidAuth",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "override",
// Module override event when an invalid access is detected, instead of each rule declaring one.
"invalidAuthorizationEvent" : "api:Home.onInvalidAuthorization",
// Default invalid action: override or redirect when an invalid access is detected, default is to redirect
"defaultAuthorizationAction" : "override",
// The validator to use for this module
"validator" : "JWTService@cbsecurity",
// Inline rules
"rules" : [ { "secureList" : "api:Secure\.*" } ]
// Full rules
// or a simple array of inline rules
"rules" : {
// Use regular expression matching on the rule match types
"useRegex" : true,
// Force SSL for all relocations
"useSSL" : false,
// A collection of default name-value pairs to add to ALL rules
// This way you can add global roles, permissions, redirects, etc
"defaults" : {},
// You can store all your rules in this inline array
"inline" : [],
// If you don't store the rules inline, then you can use a provider to load the rules
// The source can be a json file, an xml file, model, db
// Each provider can have it's appropriate properties as well. Please see the documentation for each provider.
"provider" : { "source" : "", "properties" : {} }
}
}
}
}In this page you will find a thorough overview of the capabilities of the ColdBox Security module.
For any security system, you need to know who is authenticated (authentication) and what (authorization) this user is allowed to do. cbsecurity is no different, so it provides an:
Authentication system which performs the following functions:
Validates user credentials
Logs them in and out
Tracks their security in sessions or any custom storage
Authorization system which:
Validates permissions or roles or both or none at all 😄
With CBSecurity, you can secure all your incoming ColdBox events from execution through security rules or discrete annotations within your handler's code. You will also be able to leverage our CBSecurity service model to secure any code context anywhere, from execution blocks to views and much more.
The module wraps itself around the preProcess interception point (The first execution of a ColdBox request) will try to validate if the request has been authenticated and authorized to execute.
This is done via security rules and/or annotations on the requested handler actions and through a CBSecurity Validator which knows how to authenticate and authorize the request. CBSecurity ships with many validators:
Auth Validator: this is the default validator, which provides authentication and permission-based security through our IAuthService and IAuthUser interfaces.
CFML Security Validator: ColdBox security has had this validator since version 1, and it will talk to the ColdFusion engine's security methods (cflogin,cflogout). It provides authentication and role-based security.
How does the interceptor know a user doesn't or does have access? Well, here is where you register a Validator CFC (validator setting) with the interceptor that implements two validation functions: ruleValidator() and annotationValidator() that will allow the module to know if the user is logged in and has the right authorizations to continue with the execution.
The validator has two options to determine if the user will be allowed access:
The ruleValidator() function will evaluate configured
The annotationValidator() function will look at in your handler and handler actions.
You can use rules, annotations, or even both. Rules are much more flexible and can be visualized in our security visualizer. Also, note that rules will be evaluated before annotations.
The validators' job is to tell back to the firewall if they are allowed access and if they don't, what type of validation they broke: authentication or authorization. It can also determine if the firewall should block the request.
Authenticationis when a user is NOT logged in
Authorizationis when a user does not have the right permissions to access an event/handler or action.
Once the firewall has the results and the user is NOT allowed access, the following will occur:
The request that was blocked will be logged via LogBox with the offending IP and extra metadata
If firewall database logging is turned on, we will log the block in our database logs so the visualizer can represent them.
The current requested URL will be flashed into ColdBox Flash as _securedURL so it can be used in relocations
Here are the basics of a security rule which can be defined in JSON, XML, database or CFML. Please note that only the following keys are mandatory:
securelist
Your application can be secured with security rules or handler and method annotations. Before making your choice, you should take the following arguments into consideration:
Annotations are directly visible in your code but are very static.
Annotations can protect events. Rules can protect events and incoming URLs.
Rules allow you to change your actions (override, redirect, or block) and target each rule. With annotations, you can only use your configured default action and target.
When stored in a file or database, rules can be edited by admins at runtime.
Global Rules can be declared in your config/ColdBox.cfc in plain CFML or in any module's ModuleConfig.cfc or they can come from the following global sources:
A JSON file
An XML file
The database by adding the configuration settings for it
A model by executing a getSecurityRules()
A rule is a struct that can be composed of the following elements. All of them are optional except the secureList.
Rules can be declared globally in your config/ColdBox.cfc or they can also be placed in any custom module in your application. Here is the shorthand approach to defining rules:
As you can see, you can combine all the security rule keys as you see fit. Here is the same config, but the rules will come from a JSON file:
The firewall will inspect handlers for the secured annotation. This annotation can be added to the entire handler or to an action, or both. The default value of the secured annotation is a Boolean true. This means we need a user to be authenticated in order to access it.
You can also give the annotation a value, which can be anything you like: A list of roles, a role, a list of permissions, metadata, etc. Whatever it is, this is the authorization context, and the user validator must be able to authenticate but authorize the context, or an invalid authorization will occur. Ultimately it's up to the Validator to decide what that value does and means.
By having the ability to annotate the handler and also the action, you create a cascading security model where they need to be able to access the handler first, and only then will the action be evaluated for access as well.
As we mentioned at the beginning of this overview, the security module will use a Validator object to determine if the user has authentication/authorization or not. This setting is the validator setting and will point to the WireBox ID that implements the following methods: ruleValidator() and annotationValidator(). The validator can also be selected on a per module basis as well.
Each validator must return a struct with the following keys:
allow:boolean A Boolean indicator if authentication or authorization was violated and we should block or not. True = allow, false = block
type:stringOf(authentication|authorization) A string that indicates the type of violation: authentication or authorization.
messages:string
ColdBox security ships with the AuthValidator@cbsecurity which is the default validator in the configuration setting validator. This validator can talk to ANY authentication service as long as it implements our IAuthService interface. The typical methods it calls on your authentication service are:
isLoggedIn()
getUser()
It will then also talk to the User object returned from getUser() which must implement the IAuthUser interface. The typical methods called on your User object are:
hasRole()
hasPermission()
These methods are used in order to determine authorizations.
The AuthValidator will talk to the configured authentication service to validate authentication and authorization.
This validator also ships with CBSecurity which will challenge users with browser-based basic authentication. When used, it will use whatever authentication system and user service you have configured. If you don't change the default settings, then CBSecurity will switch to using the BasicAuthUserService which allows you to store user credentials in your configuration file. Let's see how to do that:
With this configuration, the basic auth validator will allow users to log in via the browser's basic authentication. What about logging out then? Well, you have two options:
Close your browser, which clears the session
We ship with an endpoint to call for securely logging out: /cbsecurity/basicauth/logout
The second method of authentication is based on your custom security logic. You will be able to register a validation object with the module. Once a rule is matched, the module will call your validation object, send in the rule/annotation value and ask if the user can access it or not. It will be up to your logic to determine if the rule is satisfied or not. Below is a sample permission-based security validator:
The security module can distinguish between authentication issues and authorization issues. Once these actions are identified, the security module can act upon the result of these actions. These actions are based on the following 4 settings, but they all come down to three outcomes:
Relocation to another event or URL
An event override
A firewall 401 Not Authorized block
When invalid authentication or authorizations occur the interceptor will announce the following events:
cbSecurity_onInvalidAuthentication
cbSecurity_onInvalidAuthorization
You will receive the following data in the interceptData struct:
ip : The offending IP address
rule : The security rule intercepted or empty if annotations
settings : The firewall settings
cbSecurity_onFirewallBlock - When the firewall blocks an incoming request with a 403
You will receive the following data in the interceptData struct:
type : The type of block: hostheader or ipvalidation
config : The configuration structure of the rule
incomingIP
The CBSecurity model was introduced in version 2.3.0, and it provides you with a way to provide authorization checks, utility security methods, and security contexts anywhere you like: handlers, layouts, views, interceptors, and even models.
Getting access to the model is easy via our cbSecure() mixin (handlers/layouts/views/interceptors) or injecting it via WireBox:
Once injected, you can leverage it using our extraordinary methods listed below:
When certain permission context is met, if not, throws NotAuthorized
secure( permissions, [message] )
secureAll( permissions, [message] )
secureNone( permissions, [message] )
When a certain permission context is met, execute the success function/closure, else if a fail closure is defined, execute that instead.
when( permissions, success, fail )
whenAll( permissions, success, fail )
whenNone( permissions, success, fail )
Verify permissions or user equality
has( permissions ):boolean
all( permissions ):boolean
none( permissions ):boolean
secureView( permissions, successView, failView )
You can leverage the model to do the following authentication-related methods:
authenticate( username, password ) : Authenticate a user
getAuthService() : Get the configured auth service
getUserService() : Get the configured user service
You can use the following methods to assist in your programming needs:
createPassword( length:32, letters:true, numbers:true, symbols:true ) : Generate a random and secure password
getRealIP( trustUpstream : true ) : Get a request's actual IP address
getRealHost( trustUpstream : true ) : Get a request's actual hostname used
This module also ships with a security visualizer that will provide you with the following features:
Visual representation of all settings
Firewall reports
Firewall activity logs
Firewall rule simulator
You can access it via the /cbsecurity endpoint.
Here are the quick visualizer configurations:
You can also secure it by adding secured = true which will create a new rule in the firewall where authentication is required in order to access the /cbsecurity endpoint. You can also modify the security rule by leveraging the securityRule key with whatever key options you would like to add.
Important The visualizer is disabled by default. You have to manually enable it and secure it.
ColdBox Security offers a comprehensive feature set for RESTFul applications that require JSON web tokens. We offer both access and refresh token capabilities. Check out our for an in-depth overview.
BasicAuthUserService and provide you a basic user credentials storage within your configuration file. JWT Validator: If you want to use JSON Web Tokens, the JWT Validator provides authorization and authentication by validating incoming access/refresh tokens via headers for RESTFul API communications.
Custom Validator: You can define your own authentication and authorization engines and plug them into the cbsecurity framework.
prc as cbsecurity_matchedRuleThe validator results will be stored in prc as cbsecurity_validatorResults
If the type of invalidation is authentication the cbSecurity_onInvalidAuthentication interception will be announced
If the type of invalidation is authorization the cbSecurity_onInvalidAuthorization interception will be announced
If the type is authentication the default action (defaultAuthenticationAction) for that type will be executed (An override or a relocation or a firewall block).
If the type is authorization the default action (defaultAuthorizationAction) for that type will be executed (An override or a relocation or a firewall block).
Default Authorization Action: override or redirect when a user does not have enough permissions to access something
validatorResults : The validator results
annotationType : The annotation type intercepted, handler or action or empty if rule driven
processActions : A Boolean indicator that defaults to true. If you change this to false, then the interceptor won't fire the invalid actions. Usually, this means, you manually will do them.
ipValiationincomingHost : The incoming host if the type is hostHeader
secureWhen( context, [message] )
sameUser( user ):boolean
getUser() : Get the authenticated user
isLoggedIn() : Verify if a request is logged in
logout() : Logout via the configured auth service
Much more
Setting
Default
Description
invalidAuthenticationEvent
---
The global invalid authentication event or URI or URL to go if an invalid authentication occurs
defaultAuthenticationAction
redirect
Default Authentication Action: override or redirect when a user has not logged in
invalidAuthorizationEvent
---
The global invalid authorization event or URI or URL to go if an invalid authorization occurs
defaultAuthorizationAction



redirect
firewall : {
rules : [
{
// A list of white list events or Uri's patterns
"whiteList": "",
// A list of secured list events or Uri's patterns
"secureList": "",
// Are we matching a ColdBox event or the URL: event|url
"match": "event",
// Which authorization roles a user must have in order to access the secure list
"roles": "admin",
// Which authorization permsissions a user must have in order to access the secure list
"permissions": "",
// What happens if the user is not authenticated or authorized
// redirect or override or block
"action" : "redirect",
// If rule breaks, and you have a redirect it will redirect here, else use the global setting
"redirect" : "",
// If rule breaks, and you have an event, it will override it, else use the global setting
"overrideEvent" : "",
// Force SSL if using a redirect
"useSSL": false,
// Which http methods are allowed to execute the incoming event/url
"httpMethods" : "*",
// Which IP Addresses are allowed to execute the incoming event/url
"allowedIPs" : "*"
}
]
}// Secure the entire handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Do NOT secure the handler
component secured=false{
}
// Same as this, no annotation!
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
firewall : {
rules : [
{
// A list of white list events or Uri's patterns
"whiteList": "",
// A list of secured list events or Uri's patterns
"secureList": "",
// Are we matching a ColdBox event or the URL: event|url
"match": "event",
// Which authorization roles a user must have in order to access the secure list
"roles": "admin",
// Which authorization permsissions a user must have in order to access the secure list
"permissions": "",
// What happens if the user is not authenticated or authorized
// redirect or override or block
"action" : "redirect",
// If rule breaks, and you have a redirect it will redirect here, else use the global setting
"redirect" : "",
// If rule breaks, and you have an event, it will override it, else use the global setting
"overrideEvent" : "",
// Force SSL if using a redirect
"useSSL": false,
// Which http methods are allowed to execute the incoming event/url
"httpMethods" : "*",
// Which IP Addresses are allowed to execute the incoming event/url
"allowedIPs" : "*"
}
]
}// CB Security
cbSecurity : {
firewall : {
// Auto load the global security firewall automatically, else you can load it a-la-carte via the `Security` interceptor
"autoLoadFirewall" : true,
// The Global validator is an object that will validate the firewall rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "AuthValidator@cbsecurity",
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "security.login",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "security.notAuthorized",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : true,
"table" : "cbsecurity_logs",
"autoCreate" : true
},
// The global security rules defined inline
"rules" : [
{
"securelist": "^admin",
"match": "event",
"roles": "admin",
"action" : "redirect"
},
{
"securelist": "^noAction",
"match": "url",
"roles": "admin"
},
{
"securelist": "ruleActionOverride",
"match": "url",
"action" : "override",
"overrideEvent": "main.login"
},
{
"securelist": "override",
"match": "url",
"overrideEvent" : "security.login"
},
{
"securelist": "ruleActionRedirect",
"match": "url",
"redirect": "main.login"
}
]
}
};// CB Security
cbSecurity : {
firewall : {
/ Auto load the global security firewall automatically, else you can load it a-la-carte via the `Security` interceptor
"autoLoadFirewall" : true,
// The Global validator is an object that will validate the firewall rules and annotations and provide feedback on either authentication or authorization issues.
"validator" : "AuthValidator@cbsecurity",
// Activate handler/action based annotation security
"handlerAnnotationSecurity" : true,
// The global invalid authentication event or URI or URL to go if an invalid authentication occurs
"invalidAuthenticationEvent" : "security.login",
// Default Auhtentication Action: override or redirect when a user has not logged in
"defaultAuthenticationAction" : "redirect",
// The global invalid authorization event or URI or URL to go if an invalid authorization occurs
"invalidAuthorizationEvent" : "security.notAuthorized",
// Default Authorization Action: override or redirect when a user does not have enough permissions to access something
"defaultAuthorizationAction" : "redirect",
// Firewall database event logs.
"logs" : {
"enabled" : true,
"table" : "cbsecurity_logs",
"autoCreate" : true
},
"rules" : {
"useRegex" : true,
"defaults" :{ name : "" },
"provider" : {
"source" : "config/security-rules.json.cfm"
}
}
}
};// Secure this handler
component secured{
function index(event,rc,prc){}
function list(event,rc,prc){}
}
// Same as this
component secured=true{
}
// Not the same as this
component secured=false{
}
// Or this
component{
function index(event,rc,prc) secured{
}
function list(event,rc,prc) secured="list"{
}
}// Secure this handler
component secured="admin,users"{
function index(event,rc,prc) secured="list"{
}
function save(event,rc,prc) secured="write"{
}
}/**
* Copyright since 2016 by Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* All security validators must implement the following methods
*/
interface{
/**
* This function is called once an incoming event matches a security rule.
* You will receive the security rule that matched and an instance of the ColdBox controller.
*
* You must return a struct with three keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue
* - messages:string Info/debug messages
*
* @return { allow:boolean, type:string(authentication|authorization), messages:string }
*/
struct function ruleValidator( required rule, required controller );
/**
* This function is called once access to a handler/action is detected.
* You will receive the secured annotation value and an instance of the ColdBox Controller
*
* You must return a struct with three keys:
* - allow:boolean True, user can continue access, false, invalid access actions will ensue
* - type:string(authentication|authorization) The type of block that ocurred. Either an authentication or an authorization issue
* - messages:string Info/debug messages
*
* @return { allow:boolean, type:string(authentication|authorization), messages:string }
*/
struct function annotationValidator( required securedValue, required controller );
}
cbsecurity = {
validator = "AuthValidator@cbsecurity"
} cbAuth: {
userServiceClass: "UserService"
}cbsecurity = {
firewall : {
validator : "BasicAuthValidator@cbsecurity",
invalidAuthenticationEvent : "security.invalid",
invalidAuthorizationEvent : "security.notAuthorized",
rules = [
{
secureList : "^admin"
}
]
},
basicAuth : {
users : {
"lmajano" : { password : "test" }
}
}
}component singleton{
struct function ruleValidator( required rule, required controller ){
return permissionValidator( rule.permissions, controller, rule );
}
struct function annotationValidator( required securedValue, required controller ){
return permissionValidator( securedValue, controller );
}
private function permissionValidator( permissions, controller, rule ){
var results = { "allow" : false, "type" : "authentication" };
var user = getCurrentUser();
// First check if user has been authenticated.
if( user.isLoaded() AND user.isLoggedIn() ){
// Do we have the right permissions
if( len( arguments.permissions ) ){
results.allow = user.checkPermission( arguments.permission );
results.type = "authorization";
} else {
results.allow = true;
}
}
return results;
}
}// Mixin approach
cbSecure()
// Injection
property name="cbSecurity" inject="@CBSecurity";// Only allow access to user_admin
cbSecure().secure( "USER_ADMIN" );
// Only allow access if you have all of these permissions
cbSecure().secureAll( "EDITOR, POST_PUBLISH" )
// YOu must not have this permission, if you do, kick you out
cbSecure().secureNone( "FORGEBOX_USER" )
// Secure using security evaluations
// Kick out if you do not have the AUTHOR_ADMIN or you are not the same incoming author
cbSecurity.secureWhen(
cbSecurity.none( "AUTHOR_ADMIN" ) &&
!cbSecurity.sameUser( oAuthor )
)
// Secure using a closure
cbSecurity.secureWhen( ( user ) => !user.isConfirmed() );var oAuthor = authorService.getOrFail( rc.authorId );
prc.data = userService.getData();
// Run Security Contexts
cbSecure()
// Only user admin can change to the incoming role
.when( "USER_ADMIN", ( user ) => oAuthor.setRole( roleService.get( rc.roleID ) ) )
// The system admin can set a super admin
.when( "SYSTEM_ADMIN", ( user ) => oAuthor.setRole( roleService.getSystemAdmin() ) )
// Filter the data to be shown to the user
.when( "USER_READ_ONLY", ( user ) => prc.data.filter( ( i ) => !i.isClassified ) )
// Calling with a fail closure
cbSecurity.when(
"USER_ADMIN",
( user ) => user.setRole( "admin" ), //success
( user ) => relocate( "Invaliduser" ) //fail
);function edit( event, rc, prc ){
var oUser = userService.getOrFail( rc.id ?: "" );
if( !sameUser( oUser ) ){
relocate( "/users" );
}
}
<cfif cbsecure().all( "USER_ADMIN,USER_EDITOR" )>
This is only visible to user admins!
</cfif>
<cfif cbsecure().has( "SYSTEM_ADMIN" )>
<a href="/user/impersonate/#prc.user.getId()#">Impersonate User</a>
</cfif>
<cfif cbsecure().sameUser( prc.user )>
<i class="fa fa-star">This is You!</i>
</cfif>component{
function index( event, rc, prc ){
event.secureView( "USER_ADMIN", "users/admin/index", "users/index" );
}
}cbsecurity:{
/**
* --------------------------------------------------------------------------
* Security Visualizer
* --------------------------------------------------------------------------
* This is a debugging panel that when active, a developer can visualize security settings and more.
* You can use the `securityRule` to define what rule you want to use to secure the visualizer but make sure the `secured` flag is turned to true.
* You don't have to specify the `secureList` key, we will do that for you.
*/
visualizer : {
"enabled" : true,
"secured" : false,
"securityRule" : {}
},
}
