This closes out Module 15. Everything so far protected a single operation: a query, an output, a password, a stored value. Session security pulls several of those ideas together, since a stolen or predictable session identifier hands an attacker an already-authenticated session without needing to touch a password at all.
Learning Objectives
After completing this lesson, you'll be able to:
- Harden session cookies with this.sessionCookie in Application.cfc.
- Explain the difference between CFID/CFTOKEN and J2EE's JSESSIONID, and why one is more predictable than the other.
- Defeat session fixation by rotating the session identifier with sessionRotate() on successful login.
- Know a real gotcha where sessionRotate() and J2EE session variables don't actually work together.
- Perform a clean logout with sessionInvalidate() instead of manually reaching into the underlying Java session object.
How a Secure Session Lifecycle Fits Together
User Logs In
credentials verified
sessionRotate() Issues a New ID
defeats session fixation
Hardened Cookie Sent
httpOnly, secure, samesite
sessionInvalidate() on Logout
identifier fully retired
Hardening the Session Cookie
this.sessionCookie in Application.cfc configures the actual cookie the session identifier travels in.
| Key | Default | Purpose |
|---|---|---|
| httpOnly | true | Already on by default, blocks JavaScript from reading the cookie |
| secure | false | Off by default, needs to be explicitly set to true to require HTTPS |
| samesite | (engine default) | Strict, Lax, or None, controls whether the cookie is sent on cross-site requests |
| domain | false | Restricts which domain the cookie is scoped to |
| timeout | 30 years | The cookie's own expiry, separate from this.sessionTimeout's inactivity timeout |
component {
this.name = "SecureApp";
this.sessionManagement = true;
this.sessionTimeout = createTimeSpan(0, 0, 30, 0);
this.sessionCookie = {
httpOnly: true,
secure: true,
samesite: "Lax"
};
}httpOnly is already true by default, secure is the one that actually needs explicit action, since it defaults to false.
CFID/CFTOKEN vs J2EE's JSESSIONID
By default, ColdFusion tracks a session with two cookies: CFID, a sequential client identifier, and CFTOKEN, a random security token. A sequential identifier is inherently guessable. Enabling Use J2EE Session Variables on the ColdFusion Administrator's Memory Variables page switches to a single jsessionid cookie generated by the underlying Java container instead, which doesn't use CFID or CFTOKEN at all.
Defeating Session Fixation with sessionRotate()
Session fixation is an attack where someone hands a victim a known, valid session ID before they log in. If the application keeps that same ID after a successful login, the attacker now has an authenticated session too. sessionRotate() (CF10+) fixes this: call it the moment a login succeeds.
// call this immediately after a successful username/password check sessionRotate();
sessionRotate() creates a new session, copies the existing session scope's data into it, and invalidates the old one, so anything an attacker fixated beforehand is gone.
A Real Gotcha: sessionRotate() and J2EE Sessions Don't Mix
sessionRotate() only works on standard ColdFusion sessions, the ones identified by CFID/CFTOKEN. If Use J2EE Session Variables is enabled, the session is identified by jsessionid instead, and sessionRotate() does not rotate it. Calling sessionRotate() in a J2EE-session application runs without error but doesn't actually protect against fixation, the identifier that matters never changes. Pick one fixation defense and verify it actually applies to the session mode the application is running in, rather than combining both recommendations and assuming they stack.
A Clean Logout with sessionInvalidate()
sessionInvalidate() (CF10+, also available on Lucee) clears the session scope and invalidates the current session's identifiers in one call, which is simpler than manually clearing variables and reaching into the underlying Java session object.
function logoutUser() {
cflogout();
sessionInvalidate();
location(url="/login.cfm", addToken=false);
}location()'s addToken parameter defaults to true, which appends CFID/CFTOKEN/JSESSIONID to the redirect URL. Setting it to false keeps the session identifier out of the URL (and out of browser history, server logs, and the Referer header).
Common Beginner Mistakes
Setting secure: true on this.sessionCookie and assuming httpOnly also needs setting
httpOnly already defaults to true. secure is the one that actually defaults to false and needs explicit configuration.
Enabling J2EE Session Variables and still relying on sessionRotate() for fixation protection
sessionRotate() doesn't rotate jsessionid. Under J2EE session management, it's not providing the protection it appears to provide.
Leaving addToken at its default on a redirect after login or logout
addToken defaults to true, appending the session identifier directly into the URL, where it ends up in browser history and server access logs.
Clearing session variables manually without actually invalidating the session identifier
structClear(session) empties the data but leaves the same session ID valid. sessionInvalidate() (or sessionRotate() on login) is what actually changes the identifier itself.
Best Practices
- Set secure: true (and samesite) on this.sessionCookie explicitly, don't rely on defaults covering it.
- Call sessionRotate() immediately on successful login, and verify it actually applies if the app uses J2EE session variables.
- Use sessionInvalidate() (plus cflogout() if using ColdFusion's built-in login system) for a complete logout.
- Set addToken: false on any location()/cflocation redirect that happens around login or logout.
Interview Questions
What is session fixation, and how does sessionRotate() defend against it?
An attacker seeds a victim with a known, valid session ID before they log in. If that ID stays the same after login, the attacker now has an authenticated session too. sessionRotate() creates a new session, copies the data over, and invalidates the old ID, so the pre-login ID the attacker planted is no longer valid once the user authenticates.
Why might sessionRotate() fail to actually protect an application from session fixation?
If the application has Use J2EE Session Variables enabled, the session is tracked by jsessionid, and sessionRotate() only rotates CFID/CFTOKEN. It runs without error but doesn't rotate the identifier that's actually in use.
What's the practical difference between httpOnly and secure on a session cookie?
httpOnly stops JavaScript from reading the cookie, protecting it from XSS-based theft. secure stops the cookie from ever being sent over a plain HTTP connection. They defend against different threats and neither substitutes for the other.
Summary
In this lesson, you hardened session cookies with this.sessionCookie, compared CFID/CFTOKEN against J2EE's jsessionid, defeated session fixation with sessionRotate(), hit a real gotcha where rotation and J2EE sessions don't actually combine, and performed a clean logout with sessionInvalidate(). That closes out Module 15: Security.
What's Next?
Module 16 moves into performance: caching, general performance tips, and server monitoring.