Google is experimenting with the implementation of its W3C Draft “Private Network Access” with Chrome 98. This changes the browsers CORS mechanism if you try to access resources on a server located within an IP range considered as private. It might not affect you at all if the user facing part of your application is hosted on servers within a public IP range. If however your application is included in an iframe and accessed through your local intranet (e.g. via VPN) you might run into problems with Chrome. To understand the change lets have a short recap of CORS and its intentions.

A short CORS recap

Every webpage which is delivered to you through the browser has a specific origin. The origin consists of a scheme, the hostname and an optional port. Usually scripts and documents are restricted to only interact with their own origin. This is implemented by the browsers as the “Same-Origin-Policy (SOP)”. It is a crucial security mechanism preventing the execution of requests by malicious scripts which might try to access data of other webpages. Without the SOP in place, a javascript delivered by a malicious server could send requests to an application you are already authenticated. You don’t want foo.js to be able to send requests to your webmail provider, twitter account or similar things.

There are still situations where it makes sense to access a resource outside the own origin. A webpage might be served under example.org and you need to access api.example.org. Since api.example.org is outside the same-origin, we call this Cross-Origin Resource Sharing. For CORS requests the browser adds an additional “Origin” header. The server then needs to add an Access-Control-Allow-Origin header to the response. The browser checks if the origins match and if so, makes the response available to the script. It is common for servers to reply with a dynamically generated Access-Control-Allow-Origin: * header for allowed origins and omit it otherwise. Another option would be to generate an exactly matching response.

Example flow diagram of a CORS request/response cycle
Example flow diagram of a CORS request/response cycle

Keep in mind that the CORS policy doesn’t necessarily prevent requests to your backend but only prevents the script accessing the response! This is just another reason for honouring http safe methods.

The CORS policy contains a definition of “simple requests”. If we issue a request which is not considered “simple”, for example by setting the Content-Type header to application/json, the browser will try to fulfil a “preflight” request/response cycle with the server before sending the request. This works by issuing a HTTP OPTIONS request, adding information about the upcoming request with the headers Access-Control-Request-Method and Access-Control-Request-Headers. The server should then answer with the known Access-Control-Allow-Origin and Access-Control-Allow-Methods and Access-Control-Allow-Headers. The browser can then decide if it should send the real request to the server.

Example flow diagram of a CORS preflight request/response cycle
Example flow diagram of a CORS preflight request/response cycle

The Private Network Access Extension

If we have a look at common attacks in the internet, some of them could be mitigated by extending the current CORS policy with additional rules. Devices on your local machine or on your private network shouldn’t usually be accessible for scripts served from another origin. Popular examples are efforts to exploit web-interfaces of local routers via CSRF Pharming. Also the intranet landscapes of large companies might often be vulnerable for more specific targeted attacks.

By denying access to local networks per default we can close this kind of attack path. CORS is not a fix for your XSS vulnerabilities but might prevent an attacker from abusing it. The specification distinguishes between local, private and public IP ranges and follows a defined ruleset. Additionally, it can be influenced by a Content-Security-Policy directive treat-as-public-address.

Chrome preflight requests will now contain an additional CORS header for hosts on a private network: Access-Control-Request-Private-Network: true.

To fulfil this request the server needs to respond with Access-Control-Allow-Private-Network: true. If the server doesn’t add the header, the preflight request will fail and the browser will decide to block the request of the script.

Example flow diagram of preflight requests with private network access</figcaption
Example flow diagram of preflight requests with private network access

The W3C draft contains additional, more complex scenario and describes the implications on caching and proxies.

Do I need to change my configuration?

If you are in a scenario where you serve content from an IP-range considered as private in the context of CORS, you need to respond to the preflight request with the correct header. Otherwise, chrome won’t allow CORS.

There is the possibility to opt-in into chromes deprecation trial to disable the feature for Chrome on your applications by adding a token you acquire from Google. Considered the implications using CSP directives might be the sane way to go compared to Google’s deprecation token trial nonsense. Also Chrome can be configured via a local policy to allow private network requests.

This extension is a draft and currently not implemented by other browsers. Google might decide to postpone the feature later, but for now it is rolled out and affecting users that use Chrome.