Whatever message this page gives is out now! Go check it out!
X-SSL-Client-Cert-Subject) or exposes it via the CERT_SUBJECT CGI variable.SecurityManager reads the subject from the configured source.allowClientCertAuth enabled, the user is logged in automatically — no password is required.neo-security.xml under the ClientCertConfig key. Each can be overridden by a JVM system property at startup (JVM properties always win).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. |
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).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.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.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 |
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 |
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.-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=truejvm.config or your container JAVA_OPTS).neo-security.xml is preserved but ignored at runtime.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
}X-SSL-Client-Cert-Subject).CFIDE.adminapi.security.<cfset securityAPI = createObject("component", "CFIDE.adminapi.security")>
<cfset config = securityAPI.getClientCertConfig()>
<cfdump var="#config#">coldfusion.security.clientcertenabled (boolean)rootAdminEnabled (boolean)useCGI (boolean)subjectHeader (string)matchCriteria (string: "exact" or "cn")jvmOverrideEnabled, jvmOverrideRootAdminEnabled, jvmOverrideUseCGI, jvmOverrideSubjectHeader, jvmOverrideMatchCriteria (booleans)<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)>coldfusion.security.clientcertconfig 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. |
ValidationError — matchCriteria is not "exact" or "cn"ValidationError — subjectHeader is empty when enabled=true and useCGI=falseIllegalArgumentException — subjectHeader exceeds 200 characters or contains control characterscfsetup command-line tool (CFConfigUtility) exposes the full client cert config via the security category.cfsetup show securityclientCert* keys.# 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=falseCLI 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 |
clientCertMatchCriteria values other than exact or cn with a non-zero exit code.allowClientCertAuth=true.matchCriteria).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}$ |
<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
)>allowClientCertAuth=true and CAC is enabled. A placeholder value is acceptable.exact mode, CN value for cn mode).Allow Administrator Access as appropriate.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 checksubjectHeader from reaching ColdFusion directly.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.-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)# Run with JVM flag to test override
gradle test -DjvmArgs="-Dcoldfusion.security.admin.clientcert.auth=true"SecurityManagerClientCertJvmOverrideTest verifies that getClientCertConfig() returns enabled=true and jvmOverrideEnabled=true when the JVM flag is set.X-SSL-Client-Cert-Subject (or use CERT_SUBJECT).<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)><cfset securityAPI.setUser(
username = "CN=Test User,OU=QA,DC=example,DC=com",
password = "placeholder",
allowAdminAccess = true,
allowclientcertauth = true
)>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.cfmcurl -H "X-SSL-Client-Cert-Subject: CN=Test User,OU=QA,DC=example,DC=com" \
https://your-cf-host/CFIDE/administrator/index.cfm<cfset config = securityAPI.getClientCertConfig()>
<cfoutput>Enabled: #config.enabled#</cfoutput>
<cfoutput>JVM Override: #config.jvmOverrideEnabled#</cfoutput><!--- 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
)>CN=Test User,OU=QA,DC=example,DC=com will extract to Test User and match the stored username.# 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<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>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. |
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. |
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 |