Recommended Defenses:

  • Use "long" random tokens
  • Keep tokens out of URLs
  • Set the "Referrer-policy" response header

 

Do you want a cookie?

Let’s pick up with another old school vulnerability.  In the early days of the web, developers worried about browsers that didn’t support cookies or users that would turn the feature off.  Some early frameworks would automatically test for cookie support and if it was not available they would add the session token as a GET parameter within the URL.  Really, if we’re honest, some developers would use a URL parameter for the session just because they thought it was easier.  HTTP GET parameters are not generally treated as sensitive information.  They get saved in places like browser histories, server logs, and intermediate proxy servers.  On top of that, GET parameters were included by default in the “referer” header, so when a user clicked a link, the site they visit could get a copy of the session token.

The best, and most obvious defense is to never pass the session token as a GET parameter.  All major browsers support cookies so frameworks no longer try to detect cookie support.  The one place that this risk continues to be seen is during single-sign-on (SSO) exchanges.  Here, because one site cannot set a cookie for another site, it is common to see a token passed from the identity provider to the website in the URL.  This token is effectively a session token, but this use case allows other controls to protect it.  Specifically, the token from the IDP should only be allowed to be used once, and the token should expire very quickly after it was issued (five minutes or less).  A legitimate user would use the token immediately, so the five minute window is simply to account for differences in server clocks.  With these defenses in place, by the time the attacker views the token, it is already useless.

Another approach that isn’t strictly session related is to control how much of the URL is passed in referrer header.  There may be other information revealed in the URL that an attacker would like to get their hands on such as user names, object identifiers, and the sometimes even just the path itself is valuable.  In order to give the web developer control over how much of the URL would be included in the “referer” header an attribute was defined in the June 2014 version of the HTTP 1.1 specification, (RFC 7231)  https://datatracker.ietf.org/doc/html/rfc7231 named “noreferrer” (note that it does not continue the misspelling of “referrer” from the request header).  This attribute applied to individual link, form, and similar HTML tags.  Depending on the value of the attribute, the browser will send all, some, or none of the page’s URL in the referer header.  For better or worse, this attribute didn’t get a lot of use, probably because effort involved to add it to each individual tag, and it does not appear in the current version(https://datatracker.ietf.org/doc/html/rfc9112).  It could probably still be used, but it may lose support from browsers at some point in the future.

To reduce the overhead of tagging each link with a noreferrer attribute, in 2017 a formal proposal for a new response header was introduced called “referrer-policy”.  This response header applies the specified policy to all links on the page, and as with other response headers, the server or application can usually be easily configured to add this header to every response.  While there are some rare cases where the application wants the full URL to be passed as a request header, in general, websites should enable this header and eliminate the risk of information disclosure through the referer header.

Bonus Attack: Guess the Token

A separate, but related issue is a token value that can be guessed by an attacker. Obviously, if an attacker can simply try enough cookie values and stumble onto one for an active session, then they get access to the site.  Occasionally, developers will use a short token like 8 random alphanumeric characters that could theoretically be discovered through a brute-force attack, especially if the site commonly has many active sessions at a time.  A session token is essentially a short-lived password and we do not consider 8 character passwords secure.  However, since no human needs to type the token, there is no reason not to make it 16, 64, or even 128 characters long and most sites do that.  

Sometimes though, a developer tries to be more creative and takes a value associated with the user, like their email, user name, or identifier, and uses an encoded form of that as the token.  It looks random, but if the attacker can observe their own token and figure out how the value is derived, they might be able to predict the value of another user’s token.  Random tokens are superior to derived tokens for this reason.

The Weakest Link

This brings us to the final point for this segment.  The HSTS header and even shutting down the HTTP version of your website completely still might not protect you if your cookie isn’t scoped correctly.  Again, going back to the original specification section 8.2, the designers recognized that if a cookie is scoped to a parent domain, the protections for a certain subdomain might not apply.  For example, if you are running a website on “ultra-secure.example.com”, but set the scope of the session cookie to “.example.com”, then the cookie will also be visible in the traffic sent to “lol-who-cares.example.com”.  If you are considering setting the scope of a cookie to a higher level, consider that it will be at risk to vulnerabilities that exist in every other site that falls under that scope, including those that may be created in the future.

For more information about attacks that put a user's session at risk, and how to defend against them, consider the rest of the articles in this series.

Part 1 - Overview
Part 2 - Network Sniffing
Part 3 - Token Exposure (this article)
Part 4 - JavaScript Injection (XSS)
Part 5 - Blind Session Abuse
Part 6 - Post-compromise Use
Bonus 1 - JWTs: Only Slightly Worse
Bonus 2 - Device Bound Session Tokens

Errata

See a mistake?  Disagree with something?  This email address is being protected from spambots. You need JavaScript enabled to view it..