HTTP itself has no memory — every request is independent, and a server would normally have no idea two requests came from the same visitor. Sessions are how ColdFusion fakes that memory: a cookie identifies the visitor, and Session scope holds data tied to that specific identifier across all their requests.
The Application.cfc lessons already covered onSessionStart and onSessionEnd as lifecycle hooks. This lesson is about actually managing a session on purpose — turning it on, ending it deliberately, and protecting it from a real, well-known attack.
Why Use Sessions At All?
Imagine building a shopping cart without any session mechanism. Every single page would need to somehow know what's already in the cart, with no memory to rely on. The realistic options without a session are all worse: cram everything into hidden form fields and resubmit it on every page (clunky, and breaks the moment someone opens a new tab), stuff it into the URL (visible, easy to tamper with, and URLs have length limits), or look it up from a database keyed by IP address (unreliable — plenty of visitors share an IP, and plenty of single visitors' IPs change mid-visit).
Session scope solves this cleanly: the server keeps the cart in memory, tied to a cookie identifying that one visitor, for as long as they're active. Every later request just reads session.cart directly, no resubmission or lookup tricks required.
Learning Objectives
After completing this lesson, you'll be able to:
- Explain what problem sessions actually solve, and why the alternatives are worse.
- Turn on session management and configure a timeout.
- Use onSessionStart and onSessionEnd to initialize and clean up session data automatically.
- Build a simple login/logout flow using Session scope.
- End a session immediately with sessionInvalidate(), instead of waiting for a timeout.
- Explain what session fixation is, and prevent it with sessionRotate().
Turning It On
Session management is off by default and has to be explicitly enabled in Application.cfc.
component {
this.name = "MyApp";
this.sessionManagement = true;
this.sessionTimeout = createTimeSpan(0, 0, 30, 0); // 30 minutes of inactivity
}sessionTimeout resets on every request the visitor makes — it's 30 minutes of inactivity, not 30 minutes total.
onSessionStart and onSessionEnd
Once session management is on, ColdFusion calls two methods in Application.cfc automatically: onSessionStart the moment a new session begins, and onSessionEnd when it times out from inactivity. These are the natural place to initialize whatever a fresh session needs, and to clean up when it's gone.
<cfscript>
function onSessionStart() {
session.cart = [];
session.isLoggedIn = false;
}
function onSessionEnd(sessionScope, applicationScope) {
// no direct "session" reference here — only the arguments
writeLog(file = "app", type = "information", text = "Session ended for: " & arguments.sessionScope.sessionid);
}
</cfscript><cffunction name="onSessionStart" returnType="void">
<cfset session.cart = []>
<cfset session.isLoggedIn = false>
</cffunction>
<cffunction name="onSessionEnd" returnType="void">
<cfargument name="SessionScope" required="true">
<cfargument name="ApplicationScope" required="true">
<cflog file="app" type="information" text="Session ended for: #Arguments.SessionScope.sessionid#">
</cffunction>onSessionEnd can't reference session directly, and can't produce visible output either — it isn't tied to an active request. It can still log, and it receives the ending session's data as the SessionScope argument.
A Real Example: Login and Logout
Checking Login State on Other Pages
Always check structKeyExists first — a visitor who never logged in has no session.isLoggedIn key at all, and reading it directly would error. Lucee also has a dedicated sessionExists() function — it checks whether a session has been created at all, which is a different question from structKeyExists(session, "x") checking for one specific key inside an already-existing session.
Ending a Session Immediately: sessionInvalidate()
A logout button shouldn't just clear a couple of session variables and hope for the best — sessionInvalidate() clears the entire Session scope and makes the current session identifier invalid outright, added in ColdFusion 10.
<cfscript> sessionInvalidate(); location(url = "/login.cfm", addToken = false); </cfscript>
<cfscript> sessionInvalidate(); </cfscript> <cflocation url="/login.cfm" addToken="false">
sessionInvalidate() only invalidates ColdFusion's own session tracking (CFID/CFTOKEN) — if J2EE session management is enabled instead, the underlying J2EE session isn't explicitly invalidated by this call.
Session Fixation, and sessionRotate()
Session fixation is a real attack: if an attacker can get a victim to use a session identifier the attacker already knows (for example, by tricking them into clicking a link with a pre-set session ID), and the victim then logs in using that same session, the attacker's copy of that identifier becomes a valid, authenticated session too.
The fix is to issue a brand-new session identifier the moment a login succeeds, so any pre-existing identifier stops being useful. sessionRotate(), added in ColdFusion 10, does exactly that: it creates a new session, copies the current Session scope's data into it, and invalidates the old one.
<cfscript> // verify credentials first, then: session.isLoggedIn = true; session.username = form.username; sessionRotate(); // issue a fresh session id now that the visitor is authenticated </cfscript>
sessionRotate() rotates ColdFusion's own session identifiers (CFID/CFTOKEN). Like sessionInvalidate(), it doesn't rotate the underlying jsessionid if J2EE session management is enabled instead.
A Security Checklist Worth Taking Seriously
Beyond rotating session IDs on login, a few more concrete steps meaningfully reduce a session's attack surface:
- Don't rely only on the default CFID/CFTOKEN/jsessionid cookie names for anything security-sensitive — they're specifically what automated attack tools look for first.
- Set httpOnly on session-related cookies so client-side JavaScript (including anything injected via XSS) can't read them.
- Tune sessionTimeout to match the application, not just a generic default — long enough that a normal workflow doesn't get interrupted, short enough that an abandoned, still-logged-in session doesn't stay valid indefinitely.
- Exclude the login page itself from any session-based access checks, to avoid an infinite redirect loop between "you're not logged in" and the login page.
Scaling Sessions Across Multiple Servers
The default in-memory Session storage lives on a single server — fine for one server, but a real problem once an application runs behind a load balancer across multiple ColdFusion instances. The usual fix without changing any code is "sticky sessions" at the load balancer, always routing the same visitor to the same server, but that has its own scaling limits and creates an uneven load.
Modern ColdFusion supports storing sessions in an external cache server (Redis) instead of each instance's own memory, so any server in the cluster can read the same visitor's session data. This is specifically a ColdFusion-sessions feature (CFID/CFTOKEN) — it isn't available if J2EE session management is enabled instead.
A Real Concurrency Gotcha: Simultaneous Requests and a New Session
A subtle one: if a visitor's browser fires off more than one request at nearly the same instant (multiple frames, or several AJAX calls right as a page loads) at exactly the moment their session has just expired, more than one of those requests can end up running onSessionStart() concurrently, or reading Session scope before it's actually finished initializing.
The standard fix is a session-scoped lock around session initialization, forcing any request that arrives while a new session is being set up to wait until it's actually ready, rather than reading a half-initialized Session scope.
<cfscript>
function onSessionStart() {
lock(scope = "session", type = "exclusive", timeout = 10) {
session.cart = [];
session.initialized = true;
}
}
</cfscript>Common Beginner Mistakes
Reading session.isLoggedIn without checking it exists first
A visitor who has never logged in doesn't have that key in Session scope at all — reading it directly throws an error. Use structKeyExists(session, "isLoggedIn") first.
Manually deleting a few session keys as "logout" instead of calling sessionInvalidate()
This leaves the session identifier itself valid and anything else stored in Session scope untouched — sessionInvalidate() is the complete, correct way to end a session.
Never calling sessionRotate() after login
Without it, a session identifier that existed before login (potentially known to an attacker) simply becomes authenticated once the visitor logs in — the core of a session fixation attack.
Best Practices
- Call sessionRotate() immediately after a successful login, every time.
- Use sessionInvalidate() for logout, not manual key-by-key cleanup.
- Keep sessionTimeout reasonable for what the app actually needs — long enough to not frustrate users, short enough to limit how long an abandoned, logged-in session stays valid.
- Never store sensitive data like raw passwords in Session scope, even though it's server-side — store only what's needed, like a user ID.
Interview Questions
What does sessionInvalidate() do?
Clears the entire Session scope and invalidates the current session identifier (CFID/CFTOKEN) — the correct way to implement logout.
What is session fixation, and how does sessionRotate() prevent it?
Session fixation is when an attacker gets a victim to authenticate using a session identifier the attacker already knows, making the attacker's copy of it valid too. sessionRotate() issues a brand-new session identifier right after login, invalidating whatever identifier existed before, so a pre-known one becomes useless.
Does sessionInvalidate() or sessionRotate() work with J2EE sessions?
Only partially — both manage ColdFusion's own session tracking (CFID/CFTOKEN), but neither rotates or invalidates the underlying jsessionid when J2EE session management is enabled instead.
When does a session's timeout actually expire?
After the configured period of inactivity, not a fixed total duration — every request from that visitor resets the timeout clock.
Why use Session scope instead of just passing data through the URL or a hidden form field?
URL and form-based state is visible, easy to tamper with client-side, and has to be manually threaded through every single page. Session scope keeps the data server-side, tied to the visitor automatically via a cookie, with no resubmission needed.
Where is Session data stored by default?
In server memory, on whichever ColdFusion instance handled the request. This is why sessions don't automatically work across multiple servers in a cluster without either sticky sessions or external session storage.
What's the difference between onSessionStart and onApplicationStart?
onSessionStart fires once per new visitor session. onApplicationStart fires only once for the entire application's very first request (or after a restart) — it doesn't run again for every new visitor.
Can onSessionEnd display output to the user?
No — it isn't tied to an active request, so there's no page to output to. It can still log information, just not render anything visible.
Is Session scope safe to read and write without locking?
Generally yes for a single visitor's normal usage, since only that visitor's requests touch their own session — but concurrent requests from the same visitor (multiple tabs, simultaneous AJAX calls) can still race against each other, which is why onSessionStart specifically benefits from a session-scoped lock during initialization.
What would you store in Session vs Application scope for a shopping cart feature?
The cart contents themselves belong in Session, since they're specific to one visitor. Something like a shared product catalog or tax-rate table used to calculate the cart's total belongs in Application, since every visitor needs the same copy of it.
Summary
In this lesson, you turned on session management, built a login/logout flow, ended a session immediately with sessionInvalidate(), and prevented session fixation with sessionRotate() after login.
What's Next?
The next lesson covers Client Variables — data that persists across a visitor's separate visits, similar in spirit to Session but backed by longer-lived storage.