Whatever message this page gives is out now! Go check it out!

ColdFusion Client Certificate Authentication (CAC)

Last update:
May 18, 2026

Overview

Client Certificate Authentication (CAC) allows the ColdFusion Administrator and Admin API to authenticate users using X.509 client certificates instead of (or in addition to) username/password credentials. This is commonly used in environments that require smart-card or Common Access Card (CAC/PIV) authentication, where a reverse proxy (e.g., Apache, nginx, IIS) terminates mTLS, validates the certificate, and forwards the certificate subject to ColdFusion via an HTTP header or CGI variable.
How it works at a high level:
  1. A user presents a client certificate to the reverse proxy.
  2. The proxy validates the certificate and injects the certificate subject DN into a request header (e.g., X-SSL-Client-Cert-Subject) or exposes it via the CERT_SUBJECT CGI variable.
  3. ColdFusion's SecurityManager reads the subject from the configured source.
  4. The subject is compared against the configured list of authorized users (using either full DN or CN matching).
  5. If a match is found for a user who has allowClientCertAuth enabled, the user is logged in automatically — no password is required.

Configuration Options

All options are stored in neo-security.xml under the ClientCertConfig key. Each can be overridden by a JVM system property at startup (JVM properties always win).

Option Reference

Setting
Type
Default
Description
enabled
boolean
false
Master switch — enables client certificate authentication for CF Administrator.
rootAdminEnabled
boolean
false
Extends client cert auth to the root admin user.
useCGI
boolean
false
When true, reads the certificate subject from the CERT_SUBJECT CGI variable. When false, reads it from an HTTP header named by subjectHeader.
subjectHeader
string
""
Name of the HTTP header that contains the certificate subject DN (e.g., X-SSL-Client-Cert-Subject). Required when useCGI=false and enabled=true. Max 200 characters. No control characters allowed.
matchCriteria
string
"exact"
How to match the subject to a user. "exact" = full LDAP Distinguished Name match; "cn" = Common Name (CN) field only.

Behavior: enabled

When disabled, CAC login is completely bypassed and ColdFusion falls back to normal username/password authentication. When enabled, any incoming request to the Administrator or Admin API checks for a valid certificate subject first.

Behavior: rootAdminEnabled

By default, enabling enabled does not automatically allow the root admin (the single built-in administrator) to log in via certificate. Set rootAdminEnabled=true to also allow the root admin to authenticate this way. The root admin's username must match the certificate subject exactly (using the configured matchCriteria).

Behavior: useCGI- Certificate Source

Two sources are supported:
  • HTTP Header mode (useCGI=false): The reverse proxy injects the certificate subject into a custom HTTP header. The header name is configured via subjectHeader. Example: X-SSL-Client-Cert-Subject: CN=John Doe,OU=Users,DC=example,DC=com.
  • CGI Variable mode (useCGI=true): ColdFusion reads the built-in CERT_SUBJECT CGI variable (populated by the web server when mutual TLS is configured directly). No subjectHeader configuration is needed.

Behavior: matchCriteria - Subject Matching

Value
Behavior
Username format required
exact
The full LDAP Distinguished Name from the certificate must match the stored username verbatim (case-insensitive).
Valid LDAP DN, e.g., CN=John Doe,OU=Users,DC=example,DC=com
cn
Only the CN (Common Name) attribute of the subject DN is extracted and used for matching.
Any string, e.g., John Doe

JVM System Properties

JVM properties are read once at startup and always take precedence over values stored in neo-security.xml. They are useful for container/cloud deployments where configuration must be injected via environment rather than edited files.
JVM Property
Overrides
Type
coldfusion.security.admin.clientcert.auth
enabled
boolean string
coldfusion.security.rootadmin.clientcert.auth
rootAdminEnabled
boolean string
coldfusion.security.admin.clientcert.usecgi
useCGI
boolean string
coldfusion.security.admin.clientcert.subject.header
subjectHeader
string
coldfusion.security.admin.clientcert.subject.matchcriteria
matchCriteria
exact or cn
coldfusion.security.admin.clientcert.auth.force
(login page behavior)
boolean string

JVM Property: coldfusion.security.admin.clientcert.auth.force

This is a login-page-only flag. When true, the ColdFusion Administrator login page hides the password form entirely, displaying only the certificate-based login path. This is appropriate when you want to enforce certificate-only access with no fallback to password authentication.

Example JVM Arguments

-Dcoldfusion.security.admin.clientcert.auth=true
-Dcoldfusion.security.admin.clientcert.usecgi=false
-Dcoldfusion.security.admin.clientcert.subject.header=X-SSL-Client-Cert-Subject
-Dcoldfusion.security.admin.clientcert.subject.matchcriteria=exact
-Dcoldfusion.security.rootadmin.clientcert.auth=true
Add these to your ColdFusion JVM arguments (e.g., in jvm.config or your container JAVA_OPTS).
Note: When a JVM override is active, the Administrator UI displays the affected field as disabled with an "Overridden by JVM property" indicator. The stored value in neo-security.xml is preserved but ignored at runtime.

JVM Override Indicators in the API Response

getClientCertConfig() returns override-awareness flags so callers can distinguish JVM-controlled from persisted values:
{
  "enabled": true,
  "rootAdminEnabled": false,
  "useCGI": false,
  "subjectHeader": "X-SSL-Client-Cert-Subject",
  "matchCriteria": "exact",
  "jvmOverrideEnabled": true,
  "jvmOverrideRootAdminEnabled": false,
  "jvmOverrideUseCGI": false,
  "jvmOverrideSubjectHeader": false,
  "jvmOverrideMatchCriteria": false
}

Configuring via the ColdFusion Administrator UI

  1. Log in to the ColdFusion Administrator.
  2. Navigate to Security → Security Settings.
  3. Scroll to the Client Certificate Authentication section.
  4. Configure the options:
    • Check Enable Client Certificate Authentication to activate the feature.
    • Optionally check Enable for Root Admin to allow the root admin to log in via certificate.
    • Set Certificate Source to either HTTP Header or CGI Variable (CERT_SUBJECT).
    • If using HTTP Header: enter the Subject Header Name (the exact header name your proxy sends, e.g., X-SSL-Client-Cert-Subject).
    • Set Subject Match Criteria to Exact DN Match or Common Name (CN) Match.
  5. Click Submit Changes.
Note: When a field is controlled by a JVM property, it appears greyed out with a warning. You cannot override a JVM property from the UI.

Configuring via Admin API (CFML)

The Admin API exposes two functions in CFIDE.adminapi.security.

getClientCertConfig() — Read Current Configuration

<cfset securityAPI = createObject("component", "CFIDE.adminapi.security")>
<cfset config = securityAPI.getClientCertConfig()>
<cfdump var="#config#">
Required role: coldfusion.security.clientcert
Returns a struct with:
  • enabled (boolean)
  • rootAdminEnabled (boolean)
  • useCGI (boolean)
  • subjectHeader (string)
  • matchCriteria (string: "exact" or "cn")
  • jvmOverrideEnabled, jvmOverrideRootAdminEnabled, jvmOverrideUseCGI, jvmOverrideSubjectHeader, jvmOverrideMatchCriteria (booleans)

setClientCertConfig(config) - Save Configuration

<cfset securityAPI = createObject("component", "CFIDE.adminapi.security")>

<cfset config = {
  enabled: true,
  rootAdminEnabled: false,
  useCGI: false,
  subjectHeader: "X-SSL-Client-Cert-Subject",
  matchCriteria: "exact"
}>

<cfset securityAPI.setClientCertConfig(config)>
Required role: coldfusion.security.clientcert
Arguments — config struct keys:
Key
Type
Required
Description
enabled
boolean
No (default: false)
Master enable switch.
rootAdminEnabled
boolean
No (default: false)
Allow root admin cert auth.
useCGI
boolean
No (default: false)
Use CERT_SUBJECT CGI variable.
subjectHeader
string
Conditionally
HTTP header name. Required if enabled=true and useCGI=false.
matchCriteria
string
No (default: "exact")
"exact" or "cn" only.
Validation errors thrown:
  • ValidationErrormatchCriteria is not "exact" or "cn"
  • ValidationErrorsubjectHeader is empty when enabled=true and useCGI=false
  • IllegalArgumentExceptionsubjectHeader exceeds 200 characters or contains control characters

Configuring via cfsetup CLI

The cfsetup command-line tool (CFConfigUtility) exposes the full client cert config via the security category.

View Current Settings

cfsetup show security
This displays all security settings including the clientCert* keys.

Set Configuration

# Enable with HTTP header mode, exact DN matching
cfsetup set security \
  clientCertEnabled=true \
  clientCertRootAdminEnabled=false \
  clientCertUseCGI=false \
  clientCertSubjectHeader=X-SSL-Client-Cert-Subject \
  clientCertMatchCriteria=exact
# Enable with CGI variable mode
cfsetup set security \
  clientCertEnabled=true \
  clientCertUseCGI=true
# Switch to CN-only matching
cfsetup set security clientCertMatchCriteria=cn
# Disable client cert auth
cfsetup set security clientCertEnabled=false

CLI Key Reference

CLI Key
Maps to neo-security.xml
Valid Values
clientCertEnabled
ClientCertConfig.enabled
true / false
clientCertRootAdminEnabled
ClientCertConfig.rootAdminEnabled
true / false
clientCertUseCGI
ClientCertConfig.useCGI
true / false
clientCertSubjectHeader
ClientCertConfig.subjectHeader
Any string up to 200 chars
clientCertMatchCriteria
ClientCertConfig.matchCriteria
exact or cn
Other: The CLI rejects clientCertMatchCriteria values other than exact or cn with a non-zero exit code.

Adding Users for CAC Authentication

Each CF Administrator user who needs to authenticate via client certificate must be:
  1. Created (or updated) in the Administrator user store.
  2. Flagged with allowClientCertAuth=true.
  3. Have a username that matches the certificate subject (formatted per matchCriteria).

Username Formats

Mode
Username format
Length
Validation
exact
Full LDAP Distinguished Name, e.g. CN=John Doe,OU=Users,DC=example,DC=com
5–200 chars
Must be a valid LDAP DN (parsed via javax.naming.ldap.LdapName)
cn
Common Name only, e.g. John Doe
5–200 chars
Any characters allowed (length only)
Standard (non-CAC)
Alphanumeric + underscore, e.g. jdoe_admin
5–50 chars
Regex: ^[\p{Alnum}_]{5,50}$
Important: The username is matched case-insensitively against the extracted certificate subject.

Adding a CAC User via Admin API

<cfset securityAPI = createObject("component", "CFIDE.adminapi.security")>

<!--- Exact DN mode user --->
<cfset securityAPI.setUser(
  username           = "CN=John Doe,OU=Users,DC=example,DC=com",
  password           = "irrelevant_not_used_for_cert_auth",
  description        = "John Doe - CAC user",
  allowAdminAccess   = true,
  allowRDSAccess     = false,
  allowAdminAPIAccess = true,
  roles              = arrayNew(1),
  allowclientcertauth = true
)>
<!--- CN mode user --->
<cfset securityAPI.setUser(
  username           = "John Doe",
  password           = "not_used",
  description        = "John Doe - CN cert auth",
  allowAdminAccess   = true,
  allowclientcertauth = true
)>
Note: A password is still required by the API signature but is not used for authentication when allowClientCertAuth=true and CAC is enabled. A placeholder value is acceptable.

Adding a CAC User via Administrator UI

  1. Navigate to Security → CF Admin User Manager.
  2. Click Add User or select an existing user.
  3. Enter the username matching the certificate subject (full DN for exact mode, CN value for cn mode).
  4. Check the Allow Client Certificate Authentication checkbox.
  5. Set Allow Administrator Access as appropriate.
  6. Click Add/Update User.

Authentication Flow

Incoming request to /CFIDE/administrator/


isAdminClientCertAuthEnabled() == true?
│ Yes

Extract cert subject from:
- CGI CERT_SUBJECT (useCGI=true), or
- HTTP header named by subjectHeader (useCGI=false)


Apply matchCriteria:
- "exact": use full DN as-is
- "cn":    extract CN= attribute from DN


Is subject == rootAdminUserId? ──Yes──► rootAdminClientCertAuthEnabled? ──Yes──► Login as root admin
│ No                                                            │ No
▼                                                               ▼
Is subject an authorized user?                              Deny access
│ Yes

Does user have allowclientcertauth=true?
│ Yes

Login user without password check

Security Considerations

Reverse Proxy Hardening

CAC relies entirely on the reverse proxy to:
  • Validate the client certificate (chain, expiry, revocation via OCSP/CRL).
  • Inject the subject header only after successful validation.
  • Block any client-supplied value of subjectHeader from reaching ColdFusion directly.
If the proxy does not strip the subject header from client requests, an attacker can forge it and bypass authentication. Always ensure the proxy overwrites or removes the subject header before forwarding.

Header Injection

The subjectHeader value is validated on save to reject control characters (\x00\x1F, \x7F). However, trust in this header at runtime is absolute — any value present in the configured header is treated as a valid certificate subject.

Force-Only Mode

To prevent password-based fallback, combine CAC with:
  • -Dcoldfusion.security.admin.clientcert.auth.force=true (hides the password form on the login page)
  • -Dcoldfusion.security.admin.clientcert.auth=true (ensures CAC is always on regardless of stored config)

Testing the Feature

Unit Testing

JVM override behavior can be tested using the integration test harness:
# Run with JVM flag to test override
gradle test -DjvmArgs="-Dcoldfusion.security.admin.clientcert.auth=true"
The test class SecurityManagerClientCertJvmOverrideTest verifies that getClientCertConfig() returns enabled=true and jvmOverrideEnabled=true when the JVM flag is set.

Test the setup

Prerequisite setup
  1. Configure a reverse proxy to perform mutual TLS.
  2. Issue a test client certificate (or use your CAC/PIV device).
  3. Configure the proxy to forward the subject as X-SSL-Client-Cert-Subject (or use CERT_SUBJECT).
Step 1: Enable CAC in Administrator:
<cfset securityAPI = createObject("component", "CFIDE.adminapi.security")>
<cfset config = {
  enabled: true,
  rootAdminEnabled: false,
  useCGI: false,
  subjectHeader: "X-SSL-Client-Cert-Subject",
  matchCriteria: "exact"
}>
<cfset securityAPI.setClientCertConfig(config)>
Step 2 : Add a test CAC user:
<cfset securityAPI.setUser(
  username           = "CN=Test User,OU=QA,DC=example,DC=com",
  password           = "placeholder",
  allowAdminAccess   = true,
  allowclientcertauth = true
)>
Step 3: Simulate a request with the certificate subject header.
Using curl with a client certificate (where the proxy does the TLS and header injection):
curl -k --cert /path/to/client.crt --key /path/to/client.key \
  https://your-proxy/CFIDE/administrator/index.cfm
Or simulate the header directly (in a test environment where header injection is allowed by the proxy):
curl -H "X-SSL-Client-Cert-Subject: CN=Test User,OU=QA,DC=example,DC=com" \
  https://your-cf-host/CFIDE/administrator/index.cfm
Step 4: Verify via API:
<cfset config = securityAPI.getClientCertConfig()>
<cfoutput>Enabled: #config.enabled#</cfoutput>
<cfoutput>JVM Override: #config.jvmOverrideEnabled#</cfoutput>
Step 5: Test CN mode:
<!--- Switch to CN mode --->
<cfset config.matchCriteria = "cn">
<cfset securityAPI.setClientCertConfig(config)>

<!--- Add CN-mode user --->
<cfset securityAPI.setUser(
  username           = "Test User",
  password           = "placeholder",
  allowAdminAccess   = true,
  allowclientcertauth = true
)>
With CN mode, the subject CN=Test User,OU=QA,DC=example,DC=com will extract to Test User and match the stored username.

Verifying with cfsetup

# Check the current config
cfsetup show security | grep -i clientcert

# Enable for testing
cfsetup set security clientCertEnabled=true \
  clientCertSubjectHeader=X-SSL-Client-Cert-Subject \
  clientCertMatchCriteria=exact

# Verify it saved
cfsetup show security | grep -i clientcert

# Reset after tests
cfsetup set security \
  clientCertEnabled=false \
  clientCertRootAdminEnabled=false \
  clientCertUseCGI=false \
  clientCertSubjectHeader= \
  clientCertMatchCriteria=exact

Configuration Reference Summary

neo-security.xml Structure

<var name='ClientCertConfig'>
  <struct>
    <var name='enabled'><boolean value='true'/></var>
    <var name='rootAdminEnabled'><boolean value='false'/></var>
    <var name='useCGI'><boolean value='false'/></var>
    <var name='subjectHeader'><string value='X-SSL-Client-Cert-Subject'/></var>
    <var name='matchCriteria'><string value='exact'/></var>
  </struct>
</var>

Java API (SecurityManager)

Method
Signature
Description
getClientCertConfig()
→ Map
Returns current effective configuration (JVM overrides applied).
setClientCertConfig(Map)
(Map) → void
Validates, persists, and reloads configuration. Requires admin auth.
validateClientCertConfig(Map)
static (Map) → Struct
Validates and normalises raw config input. Throws IllegalArgumentException on invalid input.
isAdminClientCertAuthEnabled()
→ boolean
Returns whether CAC is currently enabled at runtime.
checkAdminUserClientCertificate()
→ String
Reads and matches the cert subject from the current request. Returns the matched username or null.
authenticateUsingClientCert(FusionContext)
protected → String
Core matching logic: extracts subject, applies matchCriteria, checks allowclientcertauth.

CFML Admin API

Function
Access
Description
getClientCertConfig()
coldfusion.security.clientcert
Returns current configuration struct.
setClientCertConfig(config)
coldfusion.security.clientcert
Saves new configuration.
setUser(..., allowclientcertauth)
Root admin only
Creates/updates a user with CAC flag.
getUser(username)
Root admin only
Returns user details including allowclientcertauth.

Troubleshooting

Symptom
Likely Cause
Resolution
CAC login not attempted
enabled=false or feature disabled by JVM
Check getClientCertConfig().enabled and JVM args
Subject header not found
Proxy not injecting header, or wrong header name
Verify subjectHeader matches the exact header the proxy sends; check proxy config
User not matched
Username mismatch between cert subject and stored user
Dump the raw header value and compare byte-for-byte with the stored username; check matchCriteria
Root admin can't log in via cert
rootAdminEnabled=false
Set rootAdminEnabled=true in config or JVM
Fields greyed out in UI
JVM properties override is active
Update JVM args; UI reflects override with orange "Overridden by JVM property" badge
ValidationError: Subject Header is required
enabled=true, useCGI=false, subjectHeader=""
Set a non-empty subjectHeader when using HTTP header mode
Invalid matchCriteria error
Value other than exact or cn was supplied
Only exact and cn are accepted
Username rejected at user creation
CAC user username doesn't match expected format
In exact mode: must be valid LDAP DN, 5–200 chars. In cn mode: any chars, 5–200 chars

Share this page

Was this page helpful?
We're glad. Tell us how this page helped.
We're sorry. Can you tell us what didn't work for you?
Thank you for your feedback. Your response will help improve this page.

On this page