iTranslated by AI
What is the Sec-Fetch-Site Header Coming to Rails 8.2?
I recently learned from DHH's post that starting from Rails 8.2, the framework will switch from the long-standing practice of using CSRF tokens to a modern CSRF protection method using the Sec-Fetch-Site header.
In Fizzy, they are apparently moving forward with the implementation in PRs like these ahead of Rails 8.2:
While the default will be based on the Sec-Fetch-Site header, it seems you can choose via configuration whether to fall back to the traditional token-based method if the header is missing.
Since I wasn't familiar with this Sec-Fetch-Site header myself, I'm going to summarize what it is and how it differs from traditional methods in this article.
What is the Sec-Fetch-Site Header?
It is one of the Fetch metadata request headers that tells you where a request originated from.
Headers prefixed with Sec- are forbidden request headers that cannot be modified from JavaScript, which makes it possible to prevent common CSRF attacks.
The primary values for the Sec-Fetch-Site header are as follows:
| Value | Meaning |
|---|---|
same-origin |
Request from the same origin |
same-site |
Request from the same site (including different subdomains) |
cross-site |
Request from a completely different site |
none |
No origin (e.g., user directly entering the URL in the address bar) |
Source:
Specific Examples
Suppose your Rails application is hosted at app.example.com.
Example 1: Normal Form Submission
A user POSTs a "profile update" from within the app.example.com application.
In this case, the browser attaches Sec-Fetch-Site: same-origin or Sec-Fetch-Site: same-site to the header.
Since it is same-origin/same-site, Rails allows the request.
In this scenario, there is no issue even without a CSRF token.
Example 2: Typical CSRF Attack
A malicious attacker places a form like the following on evil.example:
<form action="https://app.example.com/account" method="post">
<input type="hidden" name="_method" value="delete">
<input type="hidden" name="confirm" value="1">
</form>
<script>document.forms[0].submit()</script>
Since this is a request from a different site, the browser attaches Sec-Fetch-Site: cross-site to the header.
Rails rejects the cross-site request.
Therefore, the attack can be prevented even without a token.
Example 3: Legitimate Cross-site Requests such as OAuth
In cases like Google Login, where the user returns to the application via a Google page, there are plans to manage these via a whitelist using a trusted_origins option.
class ApplicationController < ActionController::Base
protect_from_forgery using: :header_only,
trusted_origins: %w[https://accounts.google.com],
with: :exception
end
Problems with Traditional Token Methods
While I believe the traditional token method is still an effective way to prevent CSRF, it has several issues, such as:
- Tokens must be generated and verified every time.
- Handling them in JavaScript or APIs can be a bit cumbersome.
- Old tokens can persist in caches and similar places.
And so on.
My impression is that this change improves these areas.
Challenges of the New Method
It seems to have many benefits, but what are the potential issues with the new method?
The following could be considered:
- CSRF cannot be prevented in older browsers or proxies that do not attach the
Sec-Fetch-Siteheader. - Clients other than browsers (mobile apps, webhooks, curl, etc.) often lack the
Sec-Fetch-Siteheader. For these, it might be necessary to consider that API mode originally didn't use CSRF, or that they should be protected by other authentication methods.
Since the Sec-Fetch-Site approach primarily focuses on CSRF via browsers, other types of requests will still require ongoing consideration for countermeasures.
Summary
- Using the
Sec-Fetch-Siteheader allows for a modern approach to CSRF protection. - It is scheduled to be introduced by default starting from Rails 8.2.
- There is room for further consideration for older browsers or access methods that do not provide
Sec-Fetch-Site(perhaps some countermeasures for these will emerge later?).
That's all! If there are any parts where my understanding is incorrect, please let me know kindly!
Discussion