Yet another reason to hate AOL®

Session hijacking, grabbing somebody else's URL and stealing their session, is one of the biggest security concerns around for anyone developing user-centric applications. Developers generally use client-side cookies to combat this problem. A lot of people hate cookies and may even have them disabled. Question, can session hacking protection be implemented without cookies?

The answer is "mostly".

NOTE: The author (me) uses Cold Fusion, so the examples will be in CF.

If all you care about is the technique, click here. If all you care about is code, click here.

Background Check

Why are Cookies Used?

Cookies offer a way to check the identity of the user by means of storing the CFID and CFTOKEN in client side cookies and using that information to uniquely identify the user. The cookies are only issued at login and therefore this technique is generally sound. One would actually have to copy the cookies off another's machine to steal their identity.

If Not Cookies, Then What?

If you don't want to use cookies you must find some other means of identifying the user. Normally, that means passing the CFID and CFTOKEN around in the URL. It is possible, therefore to copy somebody's URL into your own browser and steal their identity. Though session variables do time out after prolonged periods of inactivity (as designated by the server), there is a window of time where a hijacking could happen.

Most developers use one of two different approaches:

  1. Check the HTTP_REFERER variable for each page request and drop the session if the variable is empty.
  2. Store the IP address of the machine actually performing the login and destroy the session if a page request occurs to the same session, but from a different IP address.

1 does not work because IE does not always return a referrer. For example, when it opens a popup window under certain (?) conditions it may not return the referrer to the server. ALSO, HTTP_REFERER is a browser variable and could be hacked by anyone so inclined, AND, browsers such as Opera & iCap, etc actually give the user the ability to turn off the reporting of the HTTP_REFERER. FINALLY, in some versions of Netscape, the sub-frames of a FRAMESET do not get issued the HTTP_REFERER when they are requested (Oh yeah, I don't like Netscape® either).

2 does not work for dynamic proxies. AOL, for example, routes its users through proxies, and the particular proxy used from page request to page request might be different. Since a proxy reports its own IP address to the server, you cannot use this method without excluding AOL users from using your site. Maybe it's time to come up with another method.

<GRIPE>

If only the US Government hadn't pressured Intel to remove the serial numbers from CPU's...

</GRIPE>

So Those Two Don't Work Too Well, Now What?

So the answer is use cookies, use lots of them...

      ...on every image in fact! (jk)

Well, to be honest, we'll have to use some sort of cookie to regrab some of the AOL and dynamic proxy users. But, we can do some twiddling to make those particular users the only users that are mandated the user of cookies. Everybody else can turn off their cookies and not have to worry about session hijacking. So, it's not a total solution, but a start.

Start the Good Stuff

For this technique to work with Cold Fusion you must have session management turned on, a la:

<cfapplication name="whatever"

clientmanagement="No"

sessionmanagement="Yes"

setclientcookies="No"

sessiontimeout="#CreateTimeSpan(0,2,0,0)#">

And, it would be a good idea to have session timeout turned on, like it is set for 2 hours in the example above.

The overall idea is that you want to set a cookie that contains their IP address and set a session variable that contains the same IP address. We are able to inspect the IP address of the request VIA the cgi.remote_addr variable. In some instances, this variable contains the actual IP of the source machine. In others, if a proxy is used, for example, this variable will contain the last most proxy used before the request hit the Internet.

At the time of setting, all three variables—the cookie, the session variable, and their actual IP address—should match. So, in the future so long as their session variable IP address and the cookie IP address match, keeping in mind that we'll never send a cookie that contains anything other than their cgi.remote_addr, you'll never have to worry that the session is being hijacked.

Since all three match at the beginning of the process, if the situation arises where the cookie does exist and does not match the session variable IP, then you know for certain the session is being hijacked.

If the cookie is not set, there is no chance that the session is being hijacked if the actual IP address matches the session variable IP. Okay okay, I concede that your friend in the next cube over to whom that you just IM'd the URL could hijack your session since your both behind the same proxy, but there is no way around that using the IP address as the differentiator as we are in this implementation.

However, if there is no cookie set and the actual and session IP's do not match, the equation becomes a bit tougher. The user could be a hijacker, or the user could be behind a dynamic proxy, such as the situation with AOL where the proxy can change at any time, thus making it appear as if the originating machine has changed IP's. In this case, we have to mandate the use of cookies. There is really no other known way around the issue as of the publication date of this article.

The benefit gained using the code presented here is that we have eliminated the need to use cookies to avoid session hijacking except for users behind dynamic proxies, such as AOL. Though all users entering the session-enabled state are presented cookies, only those AOL are going to have difficulty using the site if they decline the cookie.

Here's Some Code

It is self contained and does not rely on any session variables to be created before this appears. A likely spot for this code is in APPLICATION.CFM Note that it does try to set the cookie exactly once. This single try to set a cookie fires the first time the session tokens are passed through the URL. All subsequent attempts will be filtered on the session.cookieset variable.

<!--- Session Hijacking Defense --->

<cfparam name="session.cookieset" default="0">

<cfparam name="url.cftoken" default="0">

<cfparam name="session.ipaddr" default="">

<!--- We assume cfid and cftoken at at the end of

the query string and make a query string

w/o them --->

<cfset new_query = cgi.script_name

& IIF(Len(cgi.query_string),

DE("?#cgi.query_string#"),

DE(""))>

<cfif FindNoCase("cfid=", new_query)>

<cfset new_query = Left(new_query,

FindNoCase("cfid=",

new_query) - 2)>

</cfif>

<cfif Val(url.cftoken)>

<cfif IsDefined("cookie.ipaddr")

AND LEN(session.ipaddr)>

<cfif cookie.ipaddr NEQ session.ipaddr>

<!--- session is being hijacked --->

<cflocation url="#new_query#" addtoken="No">

</cfif>

<cfelse>

<cfif session.cookieset>

<!--- This is not the first time the tokens

have appeared in the URL --->

<cfif session.ipaddr NEQ cgi.remote_addr>

<!--- either hijacking or did not accept

cookie. We need cookies in this case, so

send to a page saying as such --->

<cflocation url="#new_query#" addtoken="No">

</cfif>

<cfelse>

<!--- This is the first time the tokens have

appeared in the URL --->

<cfcookie name="ipaddr" value="#session.ipaddr#">

<cfset session.cookieset = 1>

<cfset session.ipaddr = cgi.remote_addr>

<html>

<head>

<meta http-equiv="Refresh" content="0">

</head>

<body bgcolor="#dddddd"> </body>

</html>

<cfabort>

</cfif>

</cfif>

</cfif>

<!--- /Session Hijacking Defense --->

Feedback is encouraged and welcomed for this article. Session hijacking is a long-standing problem and very few implementations of a hijacking defense system are bulletproof. If we put everybody's head together on this, we may find a solution yet.

I would like to thank .jeff again for his help in creating this document.