· 4 min read

Safer Request Headers: Fetch Metadata Request Headers

This article was auto-translated from Chinese. Some nuances may be lost in translation.

One day while developing a page, I opened DevTools to check whether the request header fields were correct and noticed a few suspicious extra headers in the request.

Notice anything unusual? Taking a closer look, why are there three headers starting with Sec-Fetch-*? This caught my attention. I hadn’t set these headers, nor had I used any libraries that would. Why were they being added automatically? My guess was that the browser was doing something behind the scenes.

After searching on Google, I discovered that this is a new draft specification called Fetch Metadata Request Headers. Currently, only Chrome adds these headers; Firefox and Safari do not.

Revisiting Same-Origin Policy (CORS)

As we all know, to prevent malicious actors from using tricky methods to steal your data, browsers restrict access conditions from certain origins—for instance, requiring Access-Control-Allow-Origin headers or preflight requests for cross-origin requests. For more details, you can refer to my previous post, “Dealing with CORS and Cookies”.

Even so, there are still flaws. For example, <img/> tags can still send cross-origin requests, allowing malicious users to bypass CORS restrictions. Of course, if the server implements basic defenses, it can still block most insecure requests.

If extra header information (metadata) could be attached when sending a request to describe where it originated, the server could respond accordingly based on these headers. That is why this draft was introduced.

Header Specifications:

Currently, there are four main headers in the draft:

1. Sec-Fetch-Dest

Indicates the destination of the request.

Possible values include audio, document, font, image, object, serviceworker, and more. This provides several benefits. With this header, the server can immediately tell whether the context of the request is legitimate. For example, if a request comes from an <img> tag but isn’t asking for an image, it is very likely an attacker, and we can immediately return an error response.

2. Sec-Fetch-Mode

Indicates the mode of the request. Main values include cors, navigate, nested-navigate, no-cors, etc., which identify the request’s mode, similar to the mode option in fetch.

With headers like Set-Fetch-User, we can also know whether the user initiated the request through an interaction (such as a click, keystroke, etc.).

3. Sec-Fetch-Site

Indicates whether the origin of the request is same-origin or cross-origin.

4. Sec-Fetch-User

This header carries a boolean value. It is only true if the request is a navigation request (where the request destination is document) and was triggered by user interaction (such as clicking a button, pressing a key, etc.). The server can use this header to determine whether the user’s method of triggering the request is legitimate.

For example, when submitting a form via a click, because the request is sent by a document and involves user interaction (clicking the button to submit), Set-Fetch-User will be ?T.

Is There a Way to Remove These Headers?

If this becomes part of the formal specification, there likely won’t be a way to remove them. In addition, you cannot manually overwrite their values because they are classified as forbidden headers.

Conclusion

In the past, achieving something similar usually required custom headers, but those could still be spoofed. Furthermore, adding custom headers triggered preflight checks, creating extra overhead. The benefit of tackling this at the specification level is that these headers cannot be easily modified, and they provide a unified standard. With these headers in place, backends can perform additional checks upon receiving requests, thereby enhancing security.

Related Posts

Explore Other Topics