w2sp: Slide 8: Problem: Gremlins in the engine

w2sp: Slide 8: Problem: Gremlins in the engine (Photo credit: Terriko)

Scanning the blogs today, I noticed an article discussing a method of implementing Stateless CSRF protection. Stateless CSRF defences are required in applications where the user has no session. That might sound a bit weird, but not all applications require sessions and their architecture may be such that they do not synchronise session data across servers.

The difference between Stateful and Stateless CSRF defences is that the former requires storing the CSRF token on the server (i.e. session data) while the latter does not, i.e. the server has zero record of any CSRF tokens. As far as the server is concerned, the number of parties with persistent knowledge of a valid token is reduced to just one – the client.

Note: I say “persistent knowledge” because you can implement a Stateless CSRF defence in one of two ways which differ only in who generates the CSRF token. The server can generate the token (the simplest for any PHP programmer), communicate it to the client, and then promptly forget about it. Alternatively, the client can itself generate the CSRF token using Javascript or, if the client is itself a server, whatever programming language is in use. Anything relying on Javascript will have security implications due to the risk of Cross-Site Scripting so leaving it to servers or non-Javascript client programming is suggested.

Let’s compare both types of CSRF protections.

Stateful CSRF Defence: Synchronizer Token Pattern

Most frameworks I can think of rely on Stateful CSRF defences so you’re all familiar with the process. The server will generate a random CSRF token. The token is stored on the server as part of the user’s session data and then communicated to the user as a hidden form field whenever they request a form containing page. It might also be passed as a header or in some other format for use in Javascript requests. If it ever occurs to you to just use the session ID as the CSRF token, please give yourself a head slap.

On all POST requests, the user will submit a valid CSRF token as part of the POST data. The server completes the CSRF defence by ensuring that the submitted token is identical to the stored copy. Unless the attacker can successfully guess the token, their CSRF attacks are rendered useless.

Stateless CSRF Defence: Double Submit Pattern

In a stateless CSRF defence, what’s really important is that requests are verified as being initiated by the client. We accomplish this by using the Double Submit method of preventing CSRF.

In a Double Submit, the client submits two tokens back to the server. The first is submitted in the POST data, the second is sent as cookie data. Since the attacker has no idea what cookie data your browser has and can’t fake cookie values in a CSRF attack, the attacker only has the ability to guess what token to inject into POST data being submitted during a CSRF attack – which won’t agree with the cookie token. The server will compare the token contained in the POST data with the token from the cookie data and check that they are identical.

Simply put, we’ve switched the token storage location. Stateful CSRF defences involve storing it in session data on the server while Stateless defences need the client to store it in a cookie (with the HttpOnly flag enabled and the cookie suitably limited by subdomain or domain). Either way, we end up with the same results – two tokens for comparison, of which one always remains unknown to the attacker.

Stateful Stateless CSRF Tokens

In the blog post I read this morning, there are two fundamental problems in the proposed stateless defence. The first was assuming that WordPress’ nonce feature made a good anti-CSRF defence model (the function doesn’t actually generate good tokens let alone real nonces). The second was not recognising its departure from the Double Submit approach.

The method suggested in the blog post generates a CSRF token by hashing together inputs which include server time in seconds, a user ID, a validity period in seconds, and a textual element like an action description. These elements are all determined by the server and they share something in common – they represent server knowledge or state. This is NOT stateless. You are still directly reliant on the server storing inputs to the token generation routine which may well be identical per user or determined by publicly accessible user information (e.g. IP address).

If you assess each input to the token individually… Time is predictable and linear. User IDs may have limited range, have been previously enumerated using a timing attack against your login logic, or be non-existent in a truly stateless application. Text descriptions are probably fixed and may be either known in advance or be subject to brute forcing or harvesting. This tells the tale of an extremely bad and dangerous generating mechanism vulnerable to brute forcing due to the lack of sufficient entropy. CSRF tokens MUST be random. If they are not randomly generated, then you are doing something wrong. In this case, the need for the convenience of a reconstructable token overrode the need for securing tokens against brute forcing.

The token is submitted with POST request data (single submit – not a double submit) and the server must then reconstruct the token in order to perform a comparison. The reconstruction requires that the server have some shared state or data harvesting which acts like a seed. Crack the seed and the defences for all users will be utterly devastated.

Double Submit and Random Tokens Are Inseparable Partners

Like most attacks, CSRF does not exist in isolation so developing a good defence requires mitigating other attacks. CSRF tokens needs to resist brute forcing by using sufficient entropy to be decently random, they must be stored securely, and you must never share tokens between HTTP and HTTPS sessions. Any good CSRF token implementation, whether stateful or stateless, should reflect those requirements with features for limiting tokens by scope and time.

Enhanced by Zemanta