Published:
October 11, 2019
Category:
Technology

Not too long ago we blogged about our (mis)adventures in finding new vulnerabilities in popular software. We briefly mentioned how cookies and WebStorage come with their own set of security benefits and challenges. By combining them, we envision a session handling solution with ludicrous security properties.

CSRF and XSS

Let’s talk briefly about two commonly found vulnerabilities of web applications:

  • Cross-Site Request Forgery (CSRF)
  • Cross-Site Scripting (XSS)

CSRF attacks work by tricking victims into submitting malicious requests to the application. These requests are submitted from the victim’s browser with the cookies of the victim. The attacker cannot receive the server response. The attack triggers state-changing behaviour on the server however (since the user is authenticated), such as creating a new user, assiging a role or changing a password, which is what the attacker is after.

XSS attacks exploit the browser’s inclination to execute all JavaScript from trusted domains in order to execute malicious scripts in the context of a logged-in victim. These scripts can target sensitive information retained by the browser and spoof page-content.

The Web is rife with methods to prevent these attacks:

  • security headers
  • anti-CSRF tokens
  • sanitizing input
  • scaping output
  • etc.

The proposed solution is not a replacement to existing solutions, but rather an additional defense mechanism. This ‘defense-in-depth’ measure leverages the best of cookies and WebStorage in order to:

  • prevent CSRF
  • make stealing sensitive information such as session-tokens/JWT unattractive.

Cookies vs. WebStorage

So what are the strengths and weaknesses of Cookies and WebStorage, and how do we use them to create something that is better than the sum of its parts?

First off: cookies. They’re old, they’re ubiquitous, and they are pretty safe as long as you use the security features. Cookies can be largely shielded from XSS attacks by enabling the ‘HttpOnly’ directive, which shields your cookies from direct JavaScript interaction.

Cookies are sent along with the HTTP headers with each browser request to their origin. If you are not using the relatively new ‘SameSite’ directive (and most applications aren’t) your browser attaches cookies to any requests to the server, regardless of request origin. Attackers can abuse this feature for CSRF attacks.

Note: setting the ‘SameSite’ directive to ‘strict’ protects your cookies from being attached to cross-origin requests. Your cookies can now no longer be abused for CSRF. The only browser that doesn’t support the ‘SameSite’ directive is Internet Explorer which is to be avoided anyway.

WebStorage was designed as a better place to store user data than cookies. WebStorage can only be accessed through JavaScript, and access is limited to the domain of the application (e.g. app.whitehats.nl). By design, WebStorage is unable to be abused for CSRF. However, by the same design, data stored in WebStorage is always vulnerable to XSS. In general it’s a good idea to encrypt sensitive data if you’re going to store it in WebStorage.

So let’s say there is a hypothetical web app that works with a session token (or a JWT for stateless auth) after user authentication. Where are you going to store this sensitive data? Are you going to choose to be more vulnerable to CSRF (cookies), or are you going to choose to increase the impact off a successfull XSS-attack (WebStorage)?

Luckily we can apply the inverse of this dilemma to achieve something secure; if CSRF can’t touch WebStorage, and XSS can’t touch cookies, why not use both?

Best of both worlds

After authentication, generate a random string (securely) and include this in the authentication token sent to the client. Take a hash (HMAC-SHA2) of the random string and a (secret) key and sent this hash to the client as well. Store the token in a cookie and the hash in LocalStorage (or vice versa).

Send both the hash and the token with each request to the server, and check the associaton between the two. The server must reject any request requiring authentication that doesn’t have the association between the two values.

This approach can be considered as a ‘defense-in-depth’ technique, meaning that the gain in security might not be worth the effort needed to implement it. Especially when considering that using modern cookie directives can achieve the same protections. Applications should be protected from CSRF and XSS attacks in the first place.