Bypassing SOP Using the Browser Cache

Misconfigured caching can lead to various vulnerabilities. For example, attackers may use badly-configured intermediate servers (reverse proxies, load balancers, or cache proxies) to gain access to sensitive data. Another way to exploit caching is through Web Cache Poisoning attacks.

The browser cache may look like a very safe place to temporarily store private information. The primary risk is that an attacker may gain access to it through the file system, which is usually considered a low-hazard vulnerability. However, in some cases, misconfigured cache-related headers may cause more serious security issues.

Cross-Domain Interaction Risks

Some websites have several subdomains and need to share data between them. This is normally not possible due to the same-origin policy (SOP). There are some methods that enable such cross-domain interaction, for example, JSONP (JSON with Padding). Developers who use such methods must implement some kind of protection against data leaking to other sites.

Let’s say that an example site has two subdomains: blog.example.com and account.example.com. The account.example.com site has a JSONP endpoint that returns sensitive user data on the basis of the user cookie. To prevent leaks, this endpoint verifies the Referer header against a whitelist that includes blog.example.com.

With this setup, if the user is lured to visit a malicious site, the attacker cannot directly steal sensitive data. However, if the JSONP endpoint sets cache-related headers, the attacker may be able to access private information from the browser cache.

Browser Behavior

Browsers have slightly different cache implementations but certain aspects are similar. First of all, only GET responses may be cached. When the browser gets the response to its GET request, it checks response headers for caching information:

  • If the response contains a Cache-Control: private or Cache-Control: public header, the response is cached for Cache-Control: max-age=<seconds>.
  • If the response contains an Expires header, the response is cached according to its value (this header has less priority than Cache-Control)
  • If none of these headers is present, some browsers may check the Last-Modified header and typically cache the response for ten percent of the difference between the current date and the Last-Modified date.
  • If there are no cache-related headers at all, the browser may cache the response but usually revalidates it before using it.

Problems may arise due to the fact that there is just one browser cache for all websites and it uses only one key to identify data: a normalized absolute URI (scheme://host:port/path?query). It means that the browser cache has no additional information about the request that initiated a particular response (for example, the site/origin from which it came, the JavaScript function or tag that initiated it, the associated cookies or headers, etc.). Any site gets the cached response from account.example.com as long as it initiates a GET request to the same URI.

The Anatomy of the Attack

The following is a step-by-step explanation of how this vulnerability is used for an attack:

  1. The user visits blog.example.com.
  2. A script on blog.example.com needs user account information.
  3. The user’s browser sends a request to the JSONP endpoint at account.example.com.
  4. The response from the JSONP endpoint at account.example.com contains cache-related headers.
  5. The user’s browser caches the response content.
  6. The user is lured to a malicious site
  7. The malicious site contains a script that points to the JSONP endpoint at account.example.com.
  8. The browser returns the cached response to the script at the malicious site.

In this situation, the Referer header is never checked because the response comes from the cache. Therefore, the attacker gains access to cached private information.

Similar Vulnerabilities

The same approach may be used to exploit other variations of Cross-Site Script Inclusion (XSSI) and other SOP Bypass attacks. Such attacks may bypass other server-side checks, for example, the Origin header, the SameSite cookie attribute, or custom headers.

Let us assume that account.example.com uses Cross-Origin Resource Sharing (CORS) instead of the JSONP endpoint. It returns an Access-Control-Allow-Origin: * header but uses a special token from a custom header to authenticate the user and protect sensitive data.

If responses are cached, the attacker may steal private information by making a request to the same URI. There is no CORS protection (due to Access-Control-Allow-Origin: *) and the user’s browser will return cached data without checking for the custom header token.

You can see how these vulnerabilities work in practice by analyzing the outputs of the browser console at a dedicated test site.

How To Protect Against SOP Bypass

The described SOP bypass vulnerability is caused by misconfiguration. In the case of cross-origin interactions, you should disable the browser cache. Most frameworks and ready-made scripts either don’t set cache-related headers or set them correctly by default (Cache-Control: no-store). However, you should always double check these headers to be secure.

Browser vendors are now considering or implementing a stricter approach to caching. Hopefully, this change will prevent such cross-origin leaks.

Share this post
Aleksei Tiurin Senior Security Researcher

Aleksei is a security researcher and pentester with a broad experience in offensive security. He is a big fan of web hacking.

Leave a Reply

Your email address will not be published.