SameSite - Defense in Depth for HTTP Cookies
Not a lot has changed in recent years with the security of HTTP cookies. As web application security testers, we have been performing a pretty standard set of tests in this area, including a check for two well-known cookie flags, HttpOnly and Secure. Recently, at Stratum Security we started adding a check for a third flag called SameSite, a new cookie attribute that promises to offer some additional security for our clients and a fresh finding for our reports.
If you hadn't noticed, the SameSite flag was introduced by Google in 2016 with little fanfare in a draft update to RFC 6265, the modern day standard for state management. Just as with HttpOnly and Secure, SameSite is a browser-based standard for managing and protecting cookies. In this case, SameSite instructs the browser to not send cookies with cross-site requests. This is important because attacks such as cross site request forgery (CSRF) and some information leakage attacks leverage weaknesses inherent in cross-site requests.
In today’s mashable web, cross-site requests play an important role. It is rare these days to see a website that doesn’t load resources such as fonts, analytics, and scripts from other sites. Cross-site requests also provide for a much better experience when browsing the web, for instance when clicking on a link to a site such as Github and being automatically logged in through an existing cookie that the browser sends with the request. Page requests across different origins also enable the pervasive and somewhat disturbing practice of tracking browsing behavior. For example, those ubiquitous and seemingly harmless social networking buttons found on web pages, send cookies on every page load back to companies like Facebook and Twitter which use the data to track what pages users are visiting.
Cross-site requests also have a darker side that attackers have been leveraging for years. CSRF is a type of an attack that relies on that fact that cookies are sent with cross-origin requests. In the classic CSRF example, a user logged into their bank is tricked by an attacker into making a hidden request to their bank in order to transfer money to the attacker. The request succeeds because the browser will send the user's cookies automatically to the banking site. Other attacks such as cross site script inclusion (XSSI) rely on a similar approach, in this case, duping users into revealing sensitive data. Another information leakage attack, known as the pixel perfect timing attack, wouldn’t be possible without cross-site requests.
As a result of these cross-site vulnerabilities, a number of remediation approaches have been developed. For example, browser settings and plugins can be used to prevent cookies from being sent across origins. Unfortunately this hasn’t gained much popularity because it makes for a bad browsing experience, by preventing automatic login as in the Github example above. Another approach is to perform a server-side check of the source of a request by examining the Origin or Referrer header. This is hit or miss since these headers may not be included with the request or they could be forged by an attacker. The most frequently recommended approach for preventing CSRF attacks is to associate a unique token with each action, something that an attacker won’t be able to access or predict. Although this approach can be highly effective, it can require significant effort to implement and maintain depending on the application.
Thus there has existed a need for a simple and more elegant way to control when cookies should be sent across origins. SameSite is such an obvious answer, one wonders why it took so long for it to be created. Simply put, if the SameSite flag is added to a cookie, a compliant browser should not send the cookie to a different origin. The format of the flag is:
Set-Cookie: key=value; HttpOnly; SameSite=strict
By allowing two values, “lax” and “strict”, the SameSite flag provides some flexibility in terms of how strict to be in restricting cross-site cookies. For sites such as Github that want to allow automatic login through GET requests, SameSite can be set with the lax value. In this case, the browser will only restrict cookies for “unsafe” requests made with POSTs. When the value is sent to strict, the cookie will not be sent for either GET or POST requests.
Obviously a site using lax would want to ensure that GETs are used for navigation and not for an unsafe state changes. If parameters used for a state change were passed in the URL, they could be a target for CSRF when the SameSite flag is set with a value of lax.
Does the simplicity and elegance of the SameSite solution mean that cross-site attacks are a thing of the past? Unfortunately not. For one, there are limited number of browser versions that support the flag as is shown here at Canius. In addition, the SameSite cookie will not prevent CSRF that occurs within the same origin. It is still possible that an attacker could carry out a CSRF attack within the same site, for example using a site's web site forum to attack an administrator of the site. In addition, SameSite does nothing to prevent XSS attacks. If an attacker is able to inject a malicious script into a site, it will be executed as coming from the victim’s origin and session cookies will still be sent with all requests the injected script makes to the domain.
In general, here at Stratum Security we think the SameSite is a great addition to the overall security of HTTP cookies. However, considering the current state of adoption and some of the limitations of SameSite, we report the lack of the SameSite flag as a best practice recommendation for our clients. SameSite is best considered as part of a defense-in-depth approach to security and developers should be encouraged to continue to deploy the standard server-side defenses against cross-site attacks to fully mitigate these risks.