Session management is a crucial part of web applications and therefore it is also the target of numerous kinds of attacks. Critical web applications, such as banking applications, require complete control of the users’ sessions to prevent abuses or session hijacking attacks.
One way to complicate these types of attack, is for the web application to have the complete control of the user session. Typically web applications use a session token, normally in the form of a cookie, to identify sessions but they do not normally check anything else to verify the legitimacy of the session. So if an attacker can retrieve the token used by an authenticated user in some manner, usually the attacker can steal the victim’s session by sending the retrieved token within each request.
However, if the web application tightly controls the state of the user inside the application, it would be very difficult for an attacker to steal his session if the user is still interacting with it. Let me explain it with an example:
Imagine an application whose flow can be represented with the following diagram:
In the initial state, users would not be logged into the application and, until they do so, they will stay in that state. After a login to the application, they get access to the private area of the application where there are 3 different pages. From any of those pages, the user can finish their session by going to the logout page. Inside the private area, users only can browse the pages in the order: 1 -> 2 -> 3 -> 1 -> 2 … Any other interaction which is not represented by an arrow in the diagram would not be allowed (for example, going from 2 to 1).
In this context, each session could be represented by the usual session token and the state which the user is in. Doing that, if an attacker was able to retrieve the token in order to attempt to gain access to the application, they would try to access a random state which potentially wouldn’t correspond with any of the following valid states.
For example, if the legitimate user is in the state “Private Page 1″ at the moment of the attack, the only 2 possible next states are “Private Page 2″ and “Log out”. An attacker would need to know this and if they did not, the application could detect the attack and invalidate the session. It is true, that this can be done by exploiting other vulnerabilities, such as a Cross-site Scripting in the application, but if so, the application will store that the new user state is, for example “Private Page 2″. Therefore, when the legitimate user tries to get access to the next state (remember that they were actually in the “Private Page 1″ state), the application will detect the irregularity and will terminate the session.
Hence, although the attacker would be able to retrieve the token and to perform several requests by discovering the state of the user, the application will identify the problem if the user continues using the application and the states of the user and the attacker become desynchronised.
In this scenario, an attacker who successfully locates another suitable vulnerability in the web application (the XSS talked above) might be able to identify the state of the user, allowing them to retrieve the token and the state and to refresh the web browser of the user by changing the session token. By doing so, the attacker would be able to use the session but the user will detect that something weird happened as their session will likely be terminated.
To avoid this situation, the application should protect against the use of concurrent logins. So when the legitimate user logs in again, the application will detect the second session of the same user and will terminate both sessions.
A limitation of this solution is that legitimate users cannot have more than one tab in the same browser on the same application because each tab would be in different states causing the application to identify the scenario as a possible attack and terminate an otherwise legitimate session. Browsers’ navigation buttons (back and forward) would provoke the same eventualities because the user could change between states in the client side “bypassing” the control of the web application.
In order to implement this, the server side application would need to be designed like a state machine where all the relationships between the states are defined. The application will need, therefore, to store the current state of the user inside his session and to check that every request came from the current state and that it is addressed to any of the states which are accessible from the current.
The tokens used to mitigate Cross-site Request Forgery (CSRF) attacks could be used to check the state where the users come from. If the token received by the application with each request matches the token sent to the user previously, this means that the user is coming from a valid state. One of the problem with this approach is that all the requests will need to include the CSRF token (including GET requests) and most libraries do not allow this check for GET requests. Another problem of the usage of the CSRF token is that the solution implemented by many frameworks consists uses one unique token which is sent through all the application but which does not change between requests. In this instance, it would not be possible to differentiate between two different states.
Each approach has its benefits and its problems so probably a mixed solution would be better, using the CSRF to preserve the valid states between requests and checking the “Referer” HTTP header to check previous states.
- Web session security can be improved by understanding web applications like a state machine and checking the state of each user in each request.
- Advantages: this approach prevents and/or complicate attacks to the session, such as session hijacking. Furthermore, it might complicate the usage of guessable/default accounts if another user is using it because concurrent sessions must be avoided.
- Disadvantages: users cannot use the application in different tabs and the interaction with it is limited to the actions offered in each page, rejecting the usage of web browsers navigation buttons.
- Two possible implementation consist of checking the “Referer” HTTP header and/or the CSRF token sent within each request.
In the next post, I will address another problem relating to web sessions management, how to identify when an user is no longer using the application, and how to close the session as soon as possible.