CFML Security: Common Vulnerabilities and How to Fix Them

ColdFusion (CFML) has been powering enterprise web applications for decades. Many legacy systems still run on it, and new CFML applications continue to be built with engines like Adobe ColdFusion and Lucee. However, CFML applications are frequently targeted by attackers — partly because many codebases were written before modern security practices became standard.

This post covers the most critical CFML security vulnerabilities I’ve encountered, along with concrete defensive measures and code fixes.

1. SQL Injection

SQL injection remains the #1 threat to CFML applications. The classic vulnerable pattern looks like this:

cfml
<!--- VULNERABLE: Never do this --->
<cfquery name="getUser" datasource="myDB">
    SELECT * FROM users
    WHERE username = '#form.username#'
    AND password = '#form.password#'
</cfquery>

An attacker can submit ' OR 1=1 -- as the username to bypass authentication entirely.

Fix: Use cfqueryparam

cfml
<!--- SECURE: Always use cfqueryparam --->
<cfquery name="getUser" datasource="myDB">
    SELECT * FROM users
    WHERE username = <cfqueryparam value="#form.username#" cfsqltype="cf_sql_varchar">
    AND password = <cfqueryparam value="#form.password#" cfsqltype="cf_sql_varchar">
</cfquery>

cfqueryparam uses prepared statements under the hood, making SQL injection impossible. Every single query variable must use cfqueryparam — no exceptions.

For dynamic table or column names (which can’t be parameterized), use a whitelist:

cfml
<cfset allowedColumns = "username,email,created_date">
<cfif NOT listFindNoCase(allowedColumns, url.sortColumn)>
    <cfset url.sortColumn = "username">
</cfif>

2. Cross-Site Scripting (XSS)

CFML does not auto-escape output by default, making XSS extremely common:

cfml
<!--- VULNERABLE --->
<cfoutput>
    Welcome, #url.name#!
</cfoutput>

Fix: Encode All Output

cfml
<!--- SECURE: Context-aware encoding --->
<cfoutput>
    <!--- HTML context --->
    Welcome, #encodeForHTML(url.name)#!

    <!--- JavaScript context --->
    <script>var name = "#encodeForJavaScript(url.name)#";</script>

    <!--- URL parameter context --->
    <a href="/profile?name=#encodeForURL(url.name)#">Profile</a>

    <!--- HTML attribute context --->
    <input type="text" value="#encodeForHTMLAttribute(url.name)#">

    <!--- CSS context --->
    <div style="background: #encodeForCSS(url.color)#;"></div>
</cfoutput>

Use the right encoding function for each context:

Context Function
HTML body encodeForHTML()
HTML attribute encodeForHTMLAttribute()
JavaScript encodeForJavaScript()
URL parameter encodeForURL()
CSS value encodeForCSS()

Enable Global Script Protection

In Application.cfc:

cfml
this.scriptProtect = "all";

This provides a basic layer of defense but should not be your only protection — it can be bypassed. Always encode output explicitly.

3. Path Traversal & File Inclusion

CFML’s powerful file operations make path traversal a serious risk:

cfml
<!--- VULNERABLE --->
<cffile action="read" file="/uploads/#url.filename#" variable="content">

<!--- An attacker submits: ../../../etc/passwd --->

Fix: Validate and Canonicalize Paths

cfml
<cfset uploadDir = expandPath("/uploads/")>
<cfset requestedFile = getCanonicalPath(uploadDir & url.filename)>

<!--- Ensure the resolved path is still within the upload directory --->
<cfif NOT requestedFile.startsWith(uploadDir)>
    <cfthrow message="Access denied: invalid file path">
</cfif>

<!--- Also validate file extension --->
<cfset allowedExtensions = "jpg,png,gif,pdf">
<cfif NOT listFindNoCase(allowedExtensions, listLast(url.filename, "."))>
    <cfthrow message="Access denied: invalid file type">
</cfif>

<cffile action="read" file="#requestedFile#" variable="content">

Also restrict cfinclude to known templates:

cfml
<!--- VULNERABLE --->
<cfinclude template="#url.page#.cfm">

<!--- SECURE: Whitelist approach --->
<cfset allowedPages = "home,about,contact,faq">
<cfif listFindNoCase(allowedPages, url.page)>
    <cfinclude template="#url.page#.cfm">
<cfelse>
    <cfinclude template="404.cfm">
</cfif>

4. Remote Code Execution via cfexecute and evaluate()

These are the most dangerous CFML functions when misused:

cfml
<!--- CRITICAL VULNERABILITY --->
<cfexecute name="#form.command#" arguments="#form.args#" timeout="10" />

<!--- CRITICAL VULNERABILITY --->
<cfset result = evaluate(url.expression)>

Fix: Eliminate Dynamic Execution

  • Never pass user input to cfexecute. If you must call system commands, hardcode the command and strictly validate arguments.
  • Never use evaluate() with user input. In modern CFML, evaluate() is almost never needed — use bracket notation instead:
cfml
<!--- Instead of evaluate("form.#fieldName#") --->
<cfset value = form[fieldName]>

<!--- Instead of evaluate("variables.config.#settingName#") --->
<cfset value = variables.config[settingName]>

In the ColdFusion Administrator or Lucee Admin, disable cfexecute entirely if it’s not needed:

text
<!--- In Application.cfc for Lucee --->
this.blockedExtTags = "cfexecute";

5. Insecure Deserialization

CFML’s deserializeJSON() and WDDX deserialization can be exploited:

cfml
<!--- VULNERABLE if input is attacker-controlled --->
<cfset data = deserializeJSON(form.payload)>

<!--- WDDX deserialization --->
<cfwddx action="wddx2cfml" input="#form.data#" output="result">

Fix: Validate Before Deserializing

cfml
<!--- Validate JSON structure against expected schema --->
<cfset rawPayload = form.payload>

<!--- Limit payload size --->
<cfif len(rawPayload) GT 10000>
    <cfthrow message="Payload too large">
</cfif>

<!--- Parse and validate structure --->
<cfset data = deserializeJSON(rawPayload)>

<!--- Verify expected keys exist and types are correct --->
<cfif NOT isStruct(data)
    OR NOT structKeyExists(data, "name")
    OR NOT isSimpleValue(data.name)>
    <cfthrow message="Invalid payload structure">
</cfif>

Avoid WDDX deserialization of user input entirely. If you need data exchange, use JSON with strict validation.

6. Authentication & Session Security

Weak Session Configuration

cfml
<!--- Application.cfc: SECURE session settings --->
component {
    this.name = "MySecureApp";
    this.sessionManagement = true;
    this.sessionTimeout = createTimeSpan(0, 0, 30, 0); // 30 minutes
    this.setClientCookies = true;

    // Use J2EE sessions — more secure than CF native sessions
    this.sessionType = "j2ee";

    function onSessionStart() {
        // Rotate session ID on login to prevent session fixation
    }

    function onRequestStart(targetPage) {
        // Set secure cookie flags
        var pc = getPageContext().getResponse();
        pc.setHeader("Set-Cookie",
            "JSESSIONID=#session.sessionid#; Path=/; HttpOnly; Secure; SameSite=Strict");
    }
}

Session Fixation Prevention

cfml
<cffunction name="onSuccessfulLogin">
    <!--- Invalidate old session and create new one --->
    <cfset sessionInvalidate()>
    <cfset sessionRotate()>

    <!--- Store user data in new session --->
    <cfset session.isLoggedIn = true>
    <cfset session.userId = authenticatedUserId>
    <cfset session.loginTime = now()>
</cffunction>

Password Storage

cfml
<!--- VULNERABLE: Plain text or simple hash --->
<cfset storedPassword = hash(form.password, "MD5")>

<!--- SECURE: Use bcrypt via Java integration --->
<cfset bcrypt = createObject("java", "org.mindrot.jbcrypt.BCrypt")>
<cfset hashedPassword = bcrypt.hashpw(form.password, bcrypt.gensalt(12))>

<!--- Verify password --->
<cfif bcrypt.checkpw(form.password, storedHash)>
    <!--- Password matches --->
</cfif>

7. CSRF Protection

cfml
<!--- Generate CSRF token --->
<cfset csrfToken = CSRFGenerateToken()>

<form method="POST" action="/update-profile">
    <input type="hidden" name="csrf_token" value="#csrfToken#">
    <!--- form fields --->
    <button type="submit">Update</button>
</form>

<!--- Verify on submission --->
<cfif NOT CSRFVerifyToken(form.csrf_token)>
    <cfthrow message="CSRF validation failed">
</cfif>

8. ColdFusion Administrator Hardening

Beyond code-level fixes, harden the server itself:

  1. Restrict Admin Access — Bind the CF Admin to localhost only or put it behind VPN/IP whitelist
  2. Disable RDS — Remote Development Service is a frequent attack vector
  3. Disable debugging output — Never expose debug info in production
  4. Update regularly — Adobe releases critical security patches frequently; Lucee likewise
  5. Remove default files — Delete /CFIDE/, /cfform/, sample apps, and documentation from production
  6. Sandbox security — Enable sandbox security to restrict file system access, tag usage, and data source access per application
  7. Disable unused services — Turn off WebSocket, Flash Remoting, and other unused features
text
<!--- In Application.cfc: disable debugging in production --->
this.debuggingEnabled = false;
this.showDebugOutput = false;

9. Security Headers

Add security headers in Application.cfc:

cfml
function onRequestStart(targetPage) {
    var response = getPageContext().getResponse();

    response.setHeader("X-Content-Type-Options", "nosniff");
    response.setHeader("X-Frame-Options", "DENY");
    response.setHeader("X-XSS-Protection", "1; mode=block");
    response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
    response.setHeader("Content-Security-Policy",
        "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");
    response.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
    response.setHeader("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
}

10. Security Audit Checklist

Use this checklist when auditing a CFML application:

  • All <cfquery> use <cfqueryparam> for every variable
  • All output is context-encoded (encodeForHTML, etc.)
  • No evaluate() with user input
  • No cfexecute with user input
  • File operations validate paths against directory traversal
  • cfinclude / cfmodule don’t accept user-controlled paths
  • Sessions use J2EE sessions with HttpOnly and Secure flags
  • CSRF tokens on all state-changing forms
  • Passwords hashed with bcrypt (not MD5/SHA)
  • CF Admin restricted and RDS disabled
  • Security headers set on all responses
  • Error handling doesn’t leak stack traces to users
  • WDDX deserialization of user input eliminated
  • ColdFusion/Lucee patched to latest version

Conclusion

CFML security isn’t fundamentally different from securing any web application — the OWASP Top 10 applies equally. The key challenges are:

  1. Legacy codebases — Many CFML apps predate modern security awareness
  2. No auto-escaping — Unlike modern frameworks, CFML requires explicit output encoding
  3. Powerful built-in functions — Features like cfexecute and evaluate() are dangerous when exposed to user input
  4. Admin interfaces — The ColdFusion Administrator is a high-value target

Start with the audit checklist above, fix SQL injection first (highest impact), then work through XSS and the remaining items. Automated tools like CFLint can help identify many of these issues in your codebase.

Security is not a one-time fix — it’s an ongoing process. Keep your CFML engine updated, review code regularly, and consider engaging a penetration testing team for critical applications.