Session Hijacking, Cold Fusion, Dynamic Proxies

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 &amp 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&gt

If only the US Government hadn't pressured Intel to remove the serial numbers from CPU's...
</GRIPE&gt

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.

Comments

Session Security with SSL

What if you establsh an encrypted connection with a security certificate (SSL) using public and secret keys. Would this not ensure that another user would not be able to hijack a session?

SSL does not prevent Session Hijacking

At least not in the manner where you simply copy the URL of one user into the browser on another computer. SSL does protect (usually) against TCP session hijacking, but that is a completely separate topic from this one and I won't go into it.

SSL will encrypt the data stream so sniffing a user's session to grab various cookie and/or URL variables wouldn't be possible.

However if you're simply looking over someone's shoulder, you can copy down the CFID and CFTOKEN (in this case) and reform the URL in your own browser and PRESTO, you can get in, even if SSL is being utilized.

One other common situation that might occur is if a user is logging in from a public computing lab of some kind. If the comptuers in the lab store browser history and the user forgets to clear it, another user may come by a few minutes later, open up the browser, look at it's history, and be able to go in and perform operations under the original user's identity. This assumes, of course, that if there is a log out feature, it was not used in the first place. You would think people would click on a logout button when they leave their computer, but after working in such a public computing lab for a couple years, I can tell you first-handed that a majority actually IGNORE the log out button.

So, in answer to your post, Sash, SSL would not protect against hijacking of a session.

-gb

SSL URL Encryption

It was my understanding that the URL's themselves are not passed encrypted even if the page itself is returned encrypted. With this in mind, it could be possible to grab someone else's session via using their URL obtained off a sniff if the session id is maintained in the URL. There was a discussion about URL encryption sometime back on thelist, and I cannot remember the exact answer, so I *hope* that the above paragraph is correct.

URLs are encrypted in SSL

I just downloaded Analyzer which is a public-domain protocol analyzer. It's a GUI Windows application that includes packet capturing capabilities. So I fired the program up, started an HTTPS/SSL session and captured it with Analyzer.

The result: URLs are encrypted in an HTTPS/SSL session. Without going into great detail, the file or directory a client requests from a web server is passed inside an HTTP header. HTTP headers are also home to where cookies are passed between client and server. These headers are encrypted during an HTTP/SSL session. This means that an attacker cannot gain any knowledge about which particular files a user is viewing on a web server. However, an attacker can ascertain the IP address of both client and server involved in the session. This isn't really a big problem though as all significant data about the session is left encrypted.

So have no fear, URL variables and cookies are all protected with SSL.

Caveat: when leaving an HTTPS/SSL session, if your web browser provides an HTTP_REFERER variable (also part of the HTTP header) then the web server you visit first after finishing your HTTPS/SSL session will be able to pick up an URL variables left over from your previous HTTPS/SSL session. Oops. This is why people must remember to use (and us web app builders remember to create) the logout feature. If you do not do this, there is a good chance that you will unknowingly be providing that first web site you visit vital information about your previous transactions. If the web server is operated by some sort of malicious administrator or someone is packet sniffing your connection, you will be giving them the tools to go back into the HTTPS/SSL session you just left and act as you.

So I hope that puts this bit to rest.

Thanks for the clarification, gb

It's good to have some scientifically collected results. And, thank you for pointing out the caveat. That is something most people probably do not consider, but is a good thing to keep in mind nonetheless. -joshua

Does this really work?

I'm right in the middle of building my old Cold Fusion system that will be utilizing session variables so this is a topic that's been weighing heavily on my mind for the past week.

I think I'm beginning to really have a firm grasp of everything that's going on and so I came back here to re-read the article and I'm not so sure now it works the way you expect it to. Perhaps someone can point me in the right direction if what I have to say below is wrong.

So basically the way this works is to set a cookie with the user's IP address at the their session started. And you're utilizing session variables to store another copy of that value on the server-side. When a user's IP doesn't match the one stored in the session variable, you then check the cookie value to see if it matches. If it matches, life goes on and in this manner you are able to support users behind dynamic/load balancing proxy systems.

Well first, you mention that the IP address is the only cookie you're setting. This isn't so however, since you're utilizing session variables, both CFTOKEN and CFID are also passed to the client as cookies. So you are actually setting 3 cookie values each time a new session begins. Secondly, the purpose of the storing the IP address is to help prevent session hijacking. This works fine until you include support for those users behind dynamic proxies. At which point, if a the IP address session variable doesn't match the user's given IP (via CGI.REMOTE_ADDR) you then refer to the user's cookie to authenticate the user is who he/she purports. Effectively, you're trusting the client to authenticate the client. So if someone is really looking hijack a session, with a packet sniffer, an attacker can easily grab the values of the cookies that are set when the session begins. With that information, an attacker could then hijack the system by simply presenting the cookie values that were packet sniffed. Voila, the intruder is in.S

Now such an attack does require a bit of sophistication. You're not worrying about some guy copying a URL with a few URL variables and giving it to his friends, not realizing what he or she is doing. In this case we're worrying about those who are proficient at cracking systems. So does a decision come into play where you have to decide what's "good enough" in terms of security? For instance a web application that simply browses a family's picture album might not need to be as secure as an application that handles online stock trading. Is that kind of decision what needs to be made here?

Or do we potentially deny users behind dynamic proxies? Simply lock them out if their IP address changes during the session. Any idea how many people that might be?

SSL doesn't make the job easier since an attacker could easily discover the user's source IP address and then form his/her own cookie values from there. However, CFID and CFTOKEN, two values that help define the session, those are kept hidden and an attacker is less likely to be able to guess those values. But they ARE guessable and neither CFID nor CFTOKEN change during the session. CFID is sequential so an attacker can enter the system legitimately right after a session begins by someone else. Take the CFID they are given, decrement by one, and now you have the previous session's CFID and can then take a stab at brute-forcing the CFTOKEN value. If your users are logged in long enough, it could give the attacker time to do this.

I'd prefer to find a solution that wasn't SSL dependant anyways. So what we need to do is somehow find a way to get the client to uniquely identify itself that isn't spoofable or instances of spoofing won't affect performance. So let me know when pigs fly.

Anyways, my big point of all this, aside from saying that if you try to cover every possible angle of attack you're going to go nuts, is that using the IP address to verify a user is probably the best route, but including support for dynamic proxies completely destroys any protection using the IP address gives you. It's not a perfect solution since an attacker could spoof IP packets and thus send commands to the server. However IP packet spoofing is getting harder as more and more network admins configure firewalls properly to deny packets leaving the network with IPs that aren't part of the network. So perhaps this is a best case possible scenario.

I wonder how Amazon does it.

Amazon, security, and unique identification

I wonder as well, though I doubt they have the ideal solution. Intel, with the introduction of its idea to digitally and uniquely stamp CPU's for this exact purpose was ridiculed and attacked by privacy junkies (this was mentioned in the article as a gripe). It is very unfortunate indeed.

I think I am going to post onto thelist for people to rally back to this article for discussion. I really appreciate the time and effort you've invested into investigating this issue, and I believe others will benefit from it.

CPU ID

An ID whether it be supplied initially by the host (session ID) or by the client (CPU ID) is still sent over the connection per each transaction therefore both will be vulnerable. Without major advances in how networking works this problem will always be present sadly. IPv6 addresses alot of the problems re: proxies/dynamic IPs, but pigs really will fly before that becomes globally implemented :(