comments (not for humans)

In this post I'll describe how OWASP Top 10: A3 - Broken Authentication and Session Management applies to javascript based applications. Problems around broken authentication and session management can happen for a number of reasons. The end result is the same. The attacker is somehow able to log in as another user, and get hold of content which the user should not have access too.

This is the risk rating from OWASP:

Threat Agents Attack Vectors Security Weakness Technical Impacts Business Impacts
______ Exploitability
AVERAGE
Prevalence
COMMON
Detectability
AVERAGE
Impact
SEVERE
______
Consider anonymous external attackers, as well as users with their own accounts, who may attempt to steal accounts from others. Also consider insiders wanting to disguise their actions. Attacker uses leaks or flaws in the authentication or session management functions (e.g., exposed accounts, passwords, session IDs) to impersonate users. Developers frequently build custom authentication and session management schemes, but building these correctly is hard. As a result, these custom schemes frequently have flaws in areas such as logout, password management, timeouts, remember me, secret question, account update, etc. Finding such flaws can sometimes be difficult, as each implementation is unique. Such flaws may allow some or even all accounts to be attacked. Once successful, the attacker can do anything the victim could do. Privileged accounts are frequently targeted. Consider the business value of the affected data or application functions.

Also consider the business impact of public exposure of the vulnerability.

Session fixation

Session fixation is an attack where the attacker is able to somehow preset the session id of antoher user. When that user logs in the attacker already has the valid session id and can use that to log in. Typically this is happening in applications that make the session ID a part of the URL: http://examples.com/;JSESSIONID=12312312312312321 This is a bad pattern. SessionIDs should never be a part of URLs. Furthermore it's important to always change the session ID when the authentication/autorization level is changing - typically during login/logout.

For javascript based web apps this typically means checking to see how the server based session components handle sessions. So typically you will want to check that whatever session framework and authentication framework you use, they should replace the sessions upon login and should not accept session IDs in URLs.

Session stealing

If the attacker steals the session of another user, the attacker can now access whatever the user has access to, because from the server side the two is the same. There are typically two ways of stealing sessions:

  • Sniffing and open wireless network
  • Cross Site Scripting

A few years back lots of websites like twitter and Facebook would only protect the login with https when the username and password was transferred. After the login, the connection would go back to http. This is a bad pattern. After login the session ID becomes a temporary replacement for your username and password, and thus we want to protect from anyone listening in. To achieve this we can make sure the browser never sends our session cookie over http, but setting the Secure flag on the cookie. This will instruct the browser never to send the cookie in http requests.

The next thing we need to do is to protect the application from Cross Site Scripting as explained in A2 - Cross Site Scripting. If an attacker can insert script into our page, the attacker could access document.cookie and steal the session cookie from there. A secondary protection mechanism we can use there, is to set the HttpOnly flag on the session cookie. This will instruct the browser not to make the session cookie available to javascript through docment.cookie. The browser will still send it in requests back to the server, but it will just not be available directly in JS.

Session guessing

It's of key importance that the server side component generating session IDs is using a cryptographically secure random function to do so. If the attacker is able to predetermine what the next session id is, the attacker could simply try different session IDs, and would at some point find a valid authenticated session, because a user logged in and got that very same session ID. I recently tested Node.js Connect's session ID generation using Burp Suite's sequencer, and Connect seems to do a decent job (the rating from Burp Suite was: Excellent).

Session timeout

Sessions should always time out after a certain period of time. Some sites use a sliding expiration, where the user is logged out if the user with the active session has been inactive for some time. Other sites use an absolute expiration, where the login is valid for a maximum of say 30 minutes. For high value sites like online banking it's probably a good idea to use both a sliding and absolute timeout. The rationale behind session timeout, is the fact that a session should be a temporary replacement for the username and password, not a permanent one. There are a couple of reasons why this is important.

If the user leaves his device or computer unattended for some time, a time out will make it harder for any attacker stealing the device to reuse the existing sessions. This can of course be partially mitigated by automatically locking the device or computer after a given period of inactivity, but this will not stop a determined attacker.

The second reason is if the session is somehow stolen, not having an absolute expiration, will allow the attacker to use that session as long as he wants - or at least until the user logs out (which many users rarely do).

Client side only sessions

Some frameworks promote client side only sessions. The way this works is that the user will log in on the server side, the server will issue a cookie which holds the session data, and send that back to the client. The benefit is of course that the javascript on the client side is now able to see and use the data in the cookie. It also frees up resources on the server, which no longer needs to maintain copies of all sessions. In a clustered backend, there is also no need to synchronize the session, as it's now being sent on every request.

However in my opinion the drawbacks outweigh the positive aspects. First of all we need to make sure the client side code cannot change the session data, because the server now inherently has to trust this cookie in later decisions. This could easily lead to a broken authentication scenario, where the user logs in as user "joesmith" and then changes the username in session to be "admin". What this means, is that the cookie needs to be signed by the server and only the server should be allowed to change it. And this can be a bit hard to get right. Naive approaches of using something like MD5(secret key + data) has been proven susceptible to length extensions attacks. Typically the developer should use something like HMAC. And we have to make sure to check the signature on the server side on each and every request to the server.

The next problem is the ability to have the session timeout. A naive approach of setting a timeout for the cookies is of course insufficient. This timeout can easily be increased by the attacker. So the timeout timestamp needs to live inside the signed session data, and be checked on the server side for each and every request.

As described under session stealing, we usually want to set the HttpOnly flag in order to protect are sessions from being stolen from javascript. This of course will not work if we also want to have benign access to that data from the client side code.

The biggest problem is however that there really is no way to log a user out. If the server side has no concept of which sessions exist, neither can it mark one as logged out. If the attacker is at some point able to steal a valid session, that session is valid in itself because of the signature, and thus the server will happily accept it until it expires. So a logout would typically mean deleting the cookie on the client side, and that's of course not sufficient if anyone else has a copy.

The last problem with client side sessions in cookies is that there is a limit on the size of the session. If you put too much data there, the server will no longer be able to respond, because the header size limit is exceeded. From javascript that means every request will result in an error and there is nothing the server can do to help the client fix the problem.

Session logout

In addition to the logout problem mentioned in the previous section, there are other logout related problems as well. Simply deleting the session cookie on the server side is not sufficient. The session should always be terminated on the server side.

Also HTML5 allows us to store more data on the client side in the Web Storage. This can greatly improve performance, but can also lead to some problems. First of all, if I'm logged in and my private data is stored in Web Storage, what happens to that data when I log out. Will the next user on the same device or computer see my data? Typically the application will use either localStorage or sessionStorage or both. In my opinion localStorage should be used for generic data like compiled JS templates or other things you may want to cache, while sessionStorage could be use for private information. However we will still need to clear out the sessionStorage during logout (and session timeout if possible). The reason is that the sessionStorage is not tied to the authenticated session in any way. SessionStorage simply means it's deleted when the browser is shutdown or restarted, which a lot of users never do unless they have to (due to a reboot or browser upgrade).

Mitigations

  • Avoid client-side only sessions unless you absolutely have to
  • Use session timeouts
  • Protect your sessions from tampering and leaks
  • Clean up session and affiliated data server side and client side on log out
  • Use a cryptographically secure random generator for session IDs
  • Don't put and/or accept session IDs in URLs
comments powered by Disqus