Evil Session Tokens

rmorschel profile image Robert Morschel ・2 min read

So you've built a new web application, SIMPLES.COM. Clients can login to the secure site, over HTTPS of course, and a session token is issued. Since we don't want the client to have to log in every time, we give the token a long lifetime, or at least a way of using it to get a new one.

The application persists the token on the browser using cookies. You did consider using browser local storage, but cookies seemed the best way to guarantee wide browser compatibility.

The application then validates every HTTP request, using the session token in the cookie, and authorises access to the client as appropriate.


Enter the hacker.

Client Joe Bloggs receives an email with a phishing link to dodgy website S1MPLES.COM (note the name is different), and clicks on it. Joe is taken to a page where a few sneaky GET requests are sent to the real SIMPLES.COM website, and Joe's browser helpfully supplies the session cookie, since it's the correct domain, and the hacker now potentially has control of Joe's account via a number of Cross-Site Scripting Attack attack vectors. (Edited previously incorrect statement about full access, which isn't applicable to this scenario - see comments below)

How do we fix this?

The first thing to do is not use cookies for authentication. The web application must attach a session token header to each request. That way the above Cross-site Request Forgery attack is not possible.

However, you now have a long-lived session token being passed around, but that's OK, because you use HTTPS.

Except you don't. Not all the time.

SIMPLES.COM is accessible over HTTP. Only the login page and secure site are via HTTPS.

So Joe, being a lover of coffee, and free WiFi, gets caught in a man-in-the-middle attack by Mr Evil with a cheap, portable WiFi router. Mr Evil intercepts the requests, and of course the session session token sent with each request.

Mr Evil now has full access to Joe's account, for a long time.

The answer is to make SIMPLES.COM full HTTPS, and to use HSTS to ensure no opportunity for man-in-the-middle exists. And to add all the recommended security headers, e.g. content security policy.

Cool. Mr Evil shrugs, and decides to pick on someone who hasn't read this article.

Much later, Mr Bored Developer is browsing through the application logs (helpfully made available via a log aggregator), when he notices that the session tokens are logged for all to see.

Not so cool. Developers are not immune to the lure of crime, even when writing Clojure.

So what do we do?

One way is to use an OAuth-style approach to issue, not one session token, but two: a short-lived access token, and a long-lived refresh token. The refresh token is stored in the application browser, but is never used for access, only to request new access tokens.

Of course these access tokens could still be leaked, but being short-lived, will expire very quickly.

And there you have it.

Posted on by:

rmorschel profile

Robert Morschel


Head of Application Security @ IG Group. I wear a cape to work.


Editor guide

Can you please elaborate how cookies can be accessed by hacker on page S1MPLES.COM when making requests to SIMPLES.COM?


Any HTTP GET request sent to SIMPLES.COM via the browser will send the cookies for SIMPLES.COM. The cookies can't be accessed, but the hacker web app can issue the get requests to retrieve potentially private data.

See en.wikipedia.org/wiki/Cross-site_r...


Unless you have a really bad CORS (en.wikipedia.org/wiki/Cross-origin...) setup, this shouldn't be possible. The entire point of CORS is to disable this type of behavior in javascript, and if this isn't being done in JS how is s1mple.com getting the information about your cookie?

Let me give an example. Head over to calhoun.io - my blog. I am using this site because I know it has jquery. Open the console in your browser and try the following:

$.getJSON( "https://www.calhoun.io", function( data ) {

This should work successfully, because you are on the same domain and CORS allows this by default. Now try the following:

$.getJSON( "https://dev.to", function( data ) {

Notice the difference? You get an error like:

XMLHttpRequest cannot load https://dev.to/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://www.calhoun.io' is therefore not allowed access.

No leaked cookies. No leaked data.

Cross site request forgery (which you link to) is something entirely different where you create a fake form on s1mple.com that POSTs to the real simple.com with nefarious data. Eg your form on s1mple.com might POST to simple.com/transfer with the payload:

{ recipient: "attacker-account", amount: 100.00 }

And then if Simple.com doesn't check for a CSRF token of its own it might perform the operation using your credentials, but your cookie and your data is never leaked to s1mple.com. It just manages to get you to perform an action that you never intended to do.

Thanks for your reply, but we've clearly crossed wires somewhere. S1MPLES.COM doesn't need to access the cookie. It just has an image that links to SIMPLES.COM with the URL of choice. The browser supplies the cookie, and hence the GET requests are authorised. No Javascript, so CORS is not applicable.

This still doesn't make sense.

Even on the owasp.org site's CSRF example with an img tag it requires the target server (eg simples.com) to perform some action to be a real vulnerability. The user's cookie is NEVER given to s1mples.com, and s1mples.com doesn't have access to the data of the user. All it can do is try to trick the user into performing actions with an already authorized cookie, but it can't actually view the data from those actions or steal the user's cookie.

I assume you are trying to help and I appreciate that, but what you are describing just doesn't line up with anything I have ever read or learned.

Do you happen to have any sources backing up your claim or documenting how a GET request with an IMG tag could lead to s1mples either (a) gaining access to a user's data, or (b) getting access to a user's cookie?

Jon, thanks for mirroring my exact thoughts. It matches all my knowledge which i consider rather solid. Still i got in doubt and was rather hesitant to oppose the claims in this post when i first read it, but it shook the foundations of my understanding how the web works.

I know the feeling. I am always hesitant to ask in case I am wrong, but I figured what the hell.

If I am wrong & the author has references to back up his claims I look silly for a bit and it adds more context for future readers like you and me. I also learn about a new attack vector as an added bonus.

If I am right, hopefully it leads to the author updating the post a bit to fix the mistake and we all live happily ever after (or until the next Heartbleed).

What you say is true, the attacker will not have access to the cookies themselves. However there are trickier vector attacks he can perform, see for example


Yes, the browser does an authenticated GET request to SIMPLE.COM. Please describe again how S1MPLE.COM gets hold of the cookie that the browser sends to SIMPLE.COM (and not S1MPLE.COM)? You are claiming that the malicious hacker web app can issue GET requests to retrieve private data. How? S1MPLE.COM can make my browser retrieve data from SIMPLE.COM into my browser. Which it does for me anyway. What is the genius idea that allows a website to steal my private data by embedding an image that links to SIMPLE.COM, when said private data never reaches that website?

Thanks for pointing out what i was about to ask. The browser sends the cookie for SIMPLE.COM to SIMPLE.COM, not S1MPLE.COM. If the malicious web app sends the GET request to SIMPLE.COM, it does not know the cookie that is set for SIMPLE.COM from the server. If S1MPLE.COM redirects the users browser to SIMPLE.COM, the browser will send to cookie to SIMPLE.COM. The browser will never send the cookie for SIMPLE.COM to S1MPLE.COM. There is no way S1MPLE.COM gets the cookie for SIMPLE.COM „helpfully supplied“ by the browser „since it is the right domain“ (what domain?!). Next up, i do not see a difference between the cookie passed around in a HTTP header and a „session token header attached to each request“. Regarding security, it is the same. Both are HTTP header values.

Then suddenly, focus switches to MITM attacks and using HTTPS instead of HTTP, which is completely orthogonal to the topic of using session cookies. Finally, session ids logged to a file only visible to the web app developer are the problem.

This conglomeration of topics feels like someone wants to spread FUD by enumerating some vague and unrelated spotlights on some security topics one might have heard of. But the scenario described here is plain wrong. Claiming that the scenario describes a CSRF attack is plain wrong. What is the rationale?


You guys are absolutely right. The line "the hacker now has access to Joe's back-end data" with respect to GET requests is very misleading in this example.

Allow me a little justification, in my defense.

At a previous company, we were CSRF hacked as follows:

  • some of our GET endpoints had side effects, which allowed the phisher to do actions on the client's account
  • we have a suite of REST endpoints for mobile clients, which the phisher was able to use because CORS can't be applied
  • we had a JS injection vulnerability that was exploited using POST, where XmlHttpRequest was sent using ENCTYPE - not sure on the details

So in our case, the phisher had full access to client data, which is not the case with SIMPLES.COM.

Thank you all. I'll edit the post appropriately.

*hangs head in abject shame


There is no shame in correcting a mistake. I'm sure I've made my fair share of them in writing. :)