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...
It is common to protect pages on your site through your CF code that runs on every request. This does not protect static files like images, plus it can still expose extra surface area of your site for attack. CommandBox has a security system to block certain paths on your site at the web server level, so these requests never reach CF.
There are currently two authentication mechanisms you can enable in CommandBox. Any combination of them can be used to secure the protected portions of your site. When CommandBox determines that the current page requires authentication, it will check each enabled auth mechanism until it finds one that can validate the user.
Basic Authentication is built on a simple username/password list and challenges the user to provide credentials in their browser. Read more here
Client Cert auth is common for internal government sites and involves the user installing a private certificate on their PC which the server asks the browser to send while negotiation the SSL connection. Client certs are a PKI cert, and often times are stored on a CAC which is a physical card issued to the user. Read more here
There are more auth mechanisms we will add in the future including Digest Auth, Header Auth, and SSO. Contact us if you're wanting to sponsor any of these.
If you do not provide an auth predicate but you enable at least one auth mechanism, ALL PAGES on the entire site will be secured. This is quite secure, but often times you only want to protect a subfolder such as /CFIDE/administrator
or /lucee/admin
.
To limit the scope of what pages required authentication, you can specify an auth predicate, which follows the same syntax as Server Rules.
We don't recommend using path()
or path-prefix()
as they are not case sensitive, which can lead to them being easily worked around if you are on Windows. Even regex()
is case-sensitive by default. Only use a case sensitive predicate matcher if your file system and web server are set to be case sensitive! If your server is case sensitive, then the example above can be simplified to this:
The most common auth predicates match a subfolder, but you are not limited to this. Anything valid in a server rule predicate can be used here, including HTTP method, headers, remote IP, etc.
CommandBox's web server supports enabling Basic Auth on your sites.
That will create the following data in your server.json
, which will be picked up the next time you start your server.
If there is no authPredicate
set, basic auth with secure ALL PAGES on the site. Once you set an authPredicate
, only the pages matching the predicate will require authentication.
The old setting location for Basic Auth (web.basicAuth
) will STILL WORK until the next major version of CommandBox, but should be considered deprecated. If both the settings exist (Ex: web.basicAuth.enable
and web.security.basicAuth.enable
), the new location will be given precedence.
The Undertow-based web server built into CommandBox will only serve up static files if they are in a list of valid extensions. This is to prevent prying eyes from hitting files they shouldn't be able to access on your server.
The current list of valid extensions is:
If you have a common static file you need to serve, you can add your own custom extensions to the list like so:
Client Cert Authentication is common for internal government sites and involves the user installing a private certificate on their PC which the server asks the browser to send while negotiation the SSL connection. Client certs are a PKI cert, and often times are stored on a CAC which is a physical card issued to the user.
It is a requirement that the user hit your site via SSL in order for their browser to send their client cert OR for you to opt into accepting upstream client cert HTTP headers from a proxy or web server that is negotiating your SSL connection. It is recommended you configure your server to either disable HTTP or automatically redirect all HTTP traffic to HTTPS, which can be enabled with the web.SSL.forceSSLRedirect
setting.
Configuring your SSL connection to accept or require client certs is covered in the SSL Client Cert portion of the docs. It's important to note configuring your server to accept SSL client certs and using these certs to authenticate a user two different things. It's possible to accept (or even require) your client (browser) to send a client cert in your web.ssl.clientCert
settings but not use the authentication mechanism to protect parts of your site automatically. Any time a browser has sent a client cert, you will be able to access those details from the CGI
and request
scopes to do your own custom authentication. It is also possible to use Client Cert authentication but not have.
If you have configured your SSL Client Cert settings to accept, but not require client certs, this will allow a user to visit your site and not be challenged to provide a cert until they hit a page that your authPredicate
kicks in and requires authentication. Only then will the user's browser prompt them for a cert.
SSL Renegotiation, when enabled, will disable HTTP/2 AND TLSv1.3. Both of these are not compatible with SSL Renegotiation.
Even if CommandBox is not using SSL directly, but is sitting behind a proxy or web server that takes care of negotiating the client cert, you can still use client cert auth to protect pages on your site if you configure CommandBox to trust any SSL_CLIENT_CERT
HTTP request header.
Only enable this setting if you TRUST your upstream proxy and you know it will overwrite any potentially-malicious SSL_CLIENT_CERT
header from the client. SSL Certs received through this header will appear in the CGI
and request
scope and will be trusted the same as a client cert directly negotiated through CommandBox, but there will be NO trust chain verification. The header will be trusted blindly from your upstream server. This setting is off by default.
You can enable the client cert auth mechanism like so:
Once enabled, all pages on your site will be secured if you have no authPredicate
configured. Otherwise, only pages that match the predicate will be secured.
By default, when you enable client cert auth, CommandBox simply checks that some client cert is present. Remember, your SSL settings require you to provide a Trust Store or a list of trusted root CAs and only client certs which are trusted by one of those CAs will be negotiated. This may be enough security for you right there if you are only allowing certs signed/trusted by a specific root cert. The client cert trust store requires you to explicitly provide every single CA you want trusted. It does NOT use your Operating system's trust store, nor does it use Java's trust store, nor does it use Lucee Servers.
If a request is authorized via a client cert, the Subject distinguished name of the client cert will be present in cgi.remote_user
just like basic auth works.
You can filter down a subset of client certs you want to be accepted for client cert auth by specifying a full or partial subject DN. This will not limit the certs which are negotiated and appear in the CGI
scope. It will only limit the certs which are allowed to provide authentication for pages secured by the authPredicate
. A Subject Distinguished Name (SDN) contains one or more parts known as Relative Distinguished Names, or RDNs. They follow an LDAP name RFC and look like this:
You may provide a single Subject DN ...
or an array of Subject DNs to filter on...
The RDNs can be in any order and you can supply a PARTIAL Subject DN. CommandBox will match all RDNs regardless of order or case sensitivity. You are not required to provide the full Subject DN, but all RDNs you provide MUST be present in the incoming cert or it will be rejected.
You can also filter down a subset of client certs you want to be accepted for client cert auth by specifying a full or partial Issuer DN. This will not limit the certs which are negotiated and appear in the CGI
scope. It will only limit the certs which are allowed to provide authentication for pages secured by the authPredicate
. An Issuer Distinguished Name (IDN) look and work the same as Subject DNs.
You may provide a single Issuer DN ...
or an array of Issuer DNs to filter on...
The RDNs can be in any order and you can supply a PARTIAL Issuer DN. CommandBox will match all RDNs regardless of order or case sensitivity. You are not required to provide the full Issuer DN, but all RDSs you provide MUST be present in the incoming cert or it will be rejected.
If you require more specific parsing of the cert pieces, then you’ll need to disable the built in security in CommandBox and write your own code that runs on every request and parses the CGI variables manually to decide if you accept the cert.
server.json
ConfigurationHere is a look at what your full config could look like in your server.json
. Note this doesn't show the web.ssl.clientCert
settings which are covered here.
CommandBox servers have a method of locking down secure URLs and or implementing any of the Undertow predicate and handlers via a nice text based language. Undertow supports a “predicate language” that allows a string to be parsed into a graph of predicates (conditions) and handlers (actions to take). Ex:
These rules can be used for any of the following:
Security - Block paths, IPs, or users
URL rewrites - Rewrite incoming URLs to something different
Modifying HTTP requests on the fly - Set headers, cookies, or response codes
Much of this functionality overlaps with the existing Tuckey-based rewrites in CommandBox, but this functionality is built directly into Undertow, has a more streamlined syntax, and allows for easier ad-hoc rules to be layered into a server that allows for you to have custom rules layered on top of built in rules. It can be used to replace what Tuckey does, or added on top.
If you have Tuckey rewrites enabled AND use the Undertow predicate-based server rules, the server rules will fire BEFORE the Tuckey rewrites.
Unlike custom Tuckey-based rewrites that must be placed in a single XML file, server rules can be provided ad-hoc in a variety of locations. They are combined and passed to the server in the order defined. This allows you to easily "layer" custom rules along with out-of-the-box lockdown profiles.
For maximum configuration options, the following mechanisms are supported for specifying the rules for a given server. Rules are processed in the order listed. i.e., a rule defined in your server.json
is processed prior to a rule in your server.default
config setting.
Ad-hoc rule array in server.json
External rules files in server.json
in the order defined
Ad-hoc rule array in config setting server.defaults
External rules files in config setting server.defaults
in the order defined
CommandBox built-in rules (web.blockCFAdmin
, web.blockSensitivePaths
)
Any module listening to server interceptions can inject their rules wherever they please in the array.
server.json
RulesYou can specify ad-hoc rules in the web.rules
property as an array of strings in your server.json
as well as specify one or more external rule files in the web.rulesFile
property as an array or list of file glob patterns.
External rule files with a .json
suffix will be expected to be a valid JSON file containing an array of strings. Ex:
External rule files with any extension OTHER than .json
will be expected to be a raw text file with one rule per line. Emtpy lines are ignored and the rules are processed in the order defined.
Rules specified directly in the server.json
or in an external JSON file must be escaped for the JSON they are a part of. Using a plain text external file can help readability since no additional escaping is required for the rules.
server.defaults
RulesLike all other config server defaults, they follow the same pattern as the server.json
file.
You can comment out any rule (whether it's in JSON or a text file) by proceeding it with a pound sign (#
).
CommandBox allows you full control over the servers you start. This includes the port and host, custom JVM args, URL rewriting, web aliases, custom error pages, and custom welcome files.
Configuration can be set at several different levels:
Passed as parameters to the server start
command
Stored in a server.json
file for that server
Global defaults in the server.defaults
config setting
Internal defaults from the ServerService
Settings will be used in that order. Also, any parameters passed to the start
command will automatically be saved to your server.json
file unless you pass the --noSaveSettings
flag.
A lot of settings used to start a server involve file paths. Paths starting with a drive letter like C:/
, a UNC network path like \\
, or a leading slash like /
are considered absolute paths and will not be expanded. Try to avoid absolute paths if you want to make your server config portable.
Paths that start with a file/folder name like foo/bar.jar
or ../../lib/my.jar
are relative and the root folder that they are relative to depends on where there are specified.
If the path is passed as a parameter to the start command, the path is relative to the current working directory
If the path is in the server.json
file, it is relative to the folder containing the JSON file. (Remember the server.json
doesn't have to be in the web root!)
If the path is in a global server.defaults
config setting, it is relative to the web root of the server.
CommandBox bundles some custom predicates and handlers to make your life easier.
The predicate cf-admin()
will returns true
if the incoming URL is to the Lucee or ColdFusion admin:
The handler block-external()
blocks any request not from localhost with a 404 response code. You can pair this with any predicate you choose.
The handler block-cf-admin()
blocks any request to the Lucee or ColdFusion admin with a 404 response code.
You can build your own predicates and handlers by compiling java classes that implement the io.undertow.predicate.PredicateBuilder
or io.undertow.server.handlers.builder.HandlerBuilder
interfaces. If the proper service metadata is present in your jar's META-INF folder, undertow will automatically find and register your builders via Java's service loader.
This isn't super difficult but involves a few moving parts. If you're interested in writing your own handlers and predicates to use in your server rules, reach out on the mailing list and we'll walk you through it.
You can see the custom handlers and predicates documented above in our Runwar source code here:
Custom handlers:
Custom Predicates:
And to test/debug your rules, start your server with the trace flag and tail the console output. Every hit in your browser will output several lines of debugging for each rule that is processed.
With the custom rule above, you'd see something like this:
If CommandBox is running a server behind a trusted proxy which is known to set the X-Forwarded-For
HTTP header, then you can enable this setting for the remote IP in your CF engine's cgi
scope to represent the upstream IP.
This is disabled by default. Otherwise a malicious client could send a fake X-Forwarded-For
HTTP header to make it look like their traffic was coming from a trusted IP such as 127.0.0.1
to bypass any IP-based restrictions. Only enable this if you trust the proxy CommandBox is sitting behind. This should never be enabled if CommandBox is directly accessible on the internet.
Enable for all servers like so:
CommandBox's server rules are based directly on the "Predicate Language" feature of JBoss Undertow. All of the official documentation for Undertow's predicate language applies to CommandBox. CommandBox doesn't limit the power of what Undertow provides at all. In fact, we give you some additional out-of-the box predicates and handlers you can use.
There are three concepts you need to understand and you'll be writing your own rules in no time.
Handler - A wrapper around the request that takes some sort of action such as returning a status code or rewriting the request.
Predicate - A conditional that evaluates to true or false. Used to Conditionally apply a handler
Exchange Attribute - An abstracted way to refer to any piece of information about the request or response. Can include URL, query string, cookies, headers, or CGI variables.
Since the parsing of your rules is handled by Undertow, it is case sensitive in many areas and CommandBox cannot control this. The following must always be lower case:
Predicate names
Handler names
Parameter names
Keywords like and
, or
, else
The full undertow docs can be found here: https://undertow.io/undertow-docs/undertow-docs-2.0.0/
A handler will take action on the request. This can redirect the request, rewrite it, abort it, or modify attributes of the request such as setting an HTTP header or status code. If the predicate is omitted, the handler will always fire for all requests. Ex:
set() - Sets an exchange attribute (see below)
rewrite() - Rewrite the request URL
redirect() - Redirect to another URL
header() - Sets an HTTP response header
set-error() - Set HTTP response code and returns the corresponding error page
ip-access-control() - Configure IP-based allow/deny rules (wildcards and IP ranges allowed)
done - Skips remaining rules
request-limit() - Limits concurrent requests
restart - Restarts process back at start of predicate rule chain (combine with rewrite, etc)
reverse-proxy() - Creates a round robin reverse proxy to a list of URLs
allowed-methods() / disallowed-methods() - Enforces whitelist/blacklist of HTTP methods on current request
A handler is called using the “name” from the docs and passing any required parameters. Params can be named or positional (if there is only one). Quoting values is only required if they have a space, comma or brace. The following are all equivalent:
Handler parameters that accept an array use the Java array literal syntax which is a comma-delimited list of values in curly braces.
More than one handler can be run by wrapping them in curly braces and using semicolons between them
A chain of handlers can be configured for the true AND false evaluation of a single predicate using the “else” keyword.
A predicate is a condition that must be matched for the handler to kick in.
path() - match the incoming request path
path-suffix() - match full file/folder names at the end of the path like file.cfm
path-prefix() - match full file/folder names at the start of the path like /lucee/admin
method() - Match the HTTP method
regex() - Match a regular expression against any exchange attribute
equals() / contains() - Do a text compare on any exchange attribute
path-template() - match a path with placeholders like /foo/{bar}
and the placeholders become variables for use in later predicates or handlers in the chain.
A predicate is called by placing parentheses after the name and passing in the required arguments as specified in the docs for that predicate. Quoting values is only required if they have a space, comma or brace. The following are all equivalent:
Complex conditions can be constructed with more than one predicate using the keywords “and” or “or”.
A predicate can be negated with the keyword “not”
Exchange attributes are an abstraction Undertow provides to reference any part of the HTTP request or response. There is a textual placeholder that can be used in predicates and handlers that will be expanded in-place.
%{REMOTE_IP} - Remote IP address
%{PROTOCOL} - Request protocol
%{METHOD} - Request method
%{REMOTE_USER} - Remote user that was authenticated
%{RESPONSE_TIME} - Time taken to process the request, in ms
%{c,cookie_name} - Any cookie value
%{q,query_param_name} - Any query parameter
%{i,request_header_name} - Any request header
%{o,response_header_name} - Any response header
${anything-here} - Any value from the predicate context (such as regex capture groups or path-template placeholders)
Use an exchange attribute like you would a variable as the parameter to a handler or predicate.
Attributes, Cookie, query params, request headers, and response header names are not case sensitive.
These are all equivalent:
There are endless combinations of predicates and handlers you can apply to your apps. Here's an assortment of examples to get you going. Many of these are contrived, but are just mean to show the range of syntax and functionality.
Rewrite a URL:
Stop processing rules for a given request:
For all GET requests, set a response header called type
with a value of get
If the incoming path ends with .css
it will rewrite the request to .xcss
and set a response header called rewritten
to true
.
Redirect all jpg requests to the same file name but with the png extension. The ${1}
exchange attribute is used to reference the first regex group.
Set a request header for all requests that you can access in your CFML app just like a normal HTTP header.
Set a Cache-Control response header only for default index pages(ie: index.html or index.cfm) in any folder.
Set a response header for a specific file.
Match certain SES-style URLs and store the place holders (referenced as exchange attributes) into HTTP request headers.
In this example, hitting the server with /restart
skips the first rule, the second rule rewrites the request and then restarts back at the first rule which fires the second time through.
Block access to a URL unless coming from a specific IP.
For more control over IP address matches, use the ip-access-control()
handler.
Leading slash is NOT required. Both of these rules are the same:
It is not required to include .*
_ at the end of a regex path unless you’ve set full-match=true
_
Perform a regex a case insensitive search like so:
When attribute values are not quoted, all leading and trailing whitespace will be trimmed. The following are all the same:
But this example is different. The leading and trailing spaces will be preserved in the path string.
Basic MVC rewrite:
Add a CORs header to every request
Rewrite requests to a sub folder based on the domain
Custom SES URL rewrite that turns 6 digit product code into a query string. (If this rule goes in your server.json
, you'll need \\
in front of the dollar sign to escape it twice. Once for the system setting expansion and once for the JSON file.)
Reject requests using an unknown host header.
Create reverse proxy