iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🏄

Understanding CSRF (Cross-Site Request Forgery) Attacks

に公開
2

This is a memo of things I looked up out of curiosity ✍

(Added Nov 3, 2022) I have made additions and corrections based on feedback received.

Why I researched this

When involved in web application development, you are often required to address a vulnerability called CSRF. However, in many cases, the framework being used handles it simply by adding a configuration, or appropriate measures have already been taken by a predecessor. Compared to its importance, there are arguably few opportunities to look closely at it in practice.

Additionally, much information has become obsolete due to changes in web browser implementations and related HTTP specifications, and I felt the hurdle was a bit high to understand the overall picture and specific countermeasures in the modern era.

Therefore, I decided to articulate and record my current understanding.
Note that I am not a web security expert but a mere developer, so there may be many errors.
I would like to correct them if pointed out 🙏

What I researched

Overview

The general name is Cross-Site Request Forgery, and it is also referred to by other names such as CSRF (Sea-surf / C-S-R-F), Request Forgery, Session Riding, and XSRF.

https://en.wikipedia.org/wiki/Cross-site_request_forgery

Literally translated, it would be something like a "site-crossing request forgery" attack.

The term "site-crossing" here carries the nuance of "(an HTTP request originated) from a place other than the target website," and refers to situations such as:

  • Viewing a website created independently by an attacker
  • Clicking a URL link contained in an email sent by an attacker
  • Clicking a URL link placed on an innocent third-party website

In the contexts above, a vulnerability that allows an HTTP request to be generated toward the target website by some method, causing an unintended update operation is called CSRF.
Possible methods include:

  • When a malicious website is displayed, a script prepared by the attacker is executed, resulting in an API request or HTTP form submission.
  • An HTTP GET request is generated from the web browser when a user clicks a URL link.

History

According to an explanation by Hiroshi Takagi, its first appearance seems to be around 2001 both domestically and internationally.

https://www.ipa.go.jp/security/vuln/event/documents/20060228_3.pdf

As for recent trends, its profile is not high compared to other vulnerabilities; for example, it was excluded from the 2021 edition of OWASP Top 10, which shows vulnerability trends published by OWASP (Open Web Application Security Project).

https://owasp.org/Top10/


Quoted from https://owasp.org/Top10/

By the way, it seems it was ranked 8th in the 2013 edition, so the number of occurrences appears to be on a downward trend.

https://owasp.org/www-pdf-archive//OWASP_LA_New_OWASP_Top_10_David_Caissy_2017_07.pdf


Quoted from https://owasp.org/www-pdf-archive//OWASP_LA_New_OWASP_Top_10_David_Caissy_2017_07.pdf (p6)

Also, in the Reporting Status of Vulnerability-related Information on Software, etc. where the IPA summarizes vulnerabilities that occurred in Japan, the cumulative number of reports and recent occurrences are not high.

https://www.ipa.go.jp/security/vuln/report/vuln2021q4.html


Quoted from https://www.ipa.go.jp/files/000095630.pdf (p18)

However, even in 2021, vulnerabilities are still being reported periodically, such as the discovery of a CSRF vulnerability in the management functions of EC-CUBE.

https://www.ec-cube.net/info/weakness/20211111/

Attack Examples

The diagram introduced in How to Build Secure Websites published by the IPA (Information-technology Promotion Agency, Japan) is quite clear.

https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_6.html


Quoted from https://www.ipa.go.jp/security/vuln/websecurity-HTML-1_6.html

To understand the attack, it's best to focus on the following points:

  • It assumes that the user of the target website is logged in through legitimate procedures.
    • The key point is that being logged in ≒ a session cookie has been issued to the user's web browser.
      • Consequently, whether or not the user is logged in to the website itself is irrelevant (described later).
  • The attack succeeds when the target website accepts an unintended update operation by design.
    • The key point is that accepting ≒ the HTTP request is processed on the backend.
      • Therefore, it doesn't matter what the result of the response to the HTTP request was.
    • Attacks that occur when a malicious response is displayed in the web browser are categorized as different attacks, such as XSS (Cross-Site Scripting).

In Japan, the "Hamachiyan incident" is often cited as a prominent CSRF attack.

https://ascii.jp/elem/000/000/063/63560/

The specific attack method was explained in detail in the following blog.
The vulnerability reportedly involved a link to a malicious website (the attacker's personal site) being posted in the diary section of an account the attacker (Mr. Hamachiya) had on the target website (mixi). When a logged-in user clicked the link, an unintended post was made to their own diary...

https://mohritaroh.hateblo.jp/entry/20050420/1113967566

Conditions for Occurrence

As already mentioned, an attack succeeds when the target website accepts an unintended update operation by design. Let's review common patterns of what specific implementations can be problematic.

POST Endpoints that Accept Form Data Submission

While modern web applications often combine SPA (Single Page Application) with arbitrary Web APIs (like REST) to fetch and update data, it was previously common to process form data sent via <form> tags on a web server and return a new page to the browser by rendering values into HTML using a template engine.

https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data

For example, suppose the following HTML is returned from the web server:

example.com/index.html
<form action="https://example.com/greeting" method="POST">
  Say: <input name="say" value="Hi"><br />
  To: <input name="to" value="Mom"><br />
  <button>Send my greetings</button>
</form>

In response, clicking the "Send my greetings" button on the form displayed in the browser sends the following HTTP request to https://example.com/greeting:


Image of the form display

HTTP Request
POST /greeting HTTP/2.0
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom

While this behavior itself is common and not problematic, the specification for HTML form submission dictates that a request will be made to the URL specified in action regardless of which domain index.html was delivered from. This is one of the causes of CSRF attacks.

https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#form-submission-algorithm

This behavior is not desirable considering modern use cases, but it seems to have been maintained because many web specifications have been revised with a strong focus on backward compatibility.

https://standards.mitsue.co.jp/archives/001233.html

If exploited, for example, by publishing the following HTML on evil.com and somehow getting a user to visit evil.com, an attacker can send any unintended POST request to example.com on behalf of the user.

evil.com/index.html
<form action="https://example.com/greeting" method="POST">
  <input name="say" value="Go away!!!!!">
  <input name="to" value="Dad">
</form>
<script>
  // Immediately execute form submission
  document.querySelector('form').submit();
</script>

If https://example.com/greeting were an endpoint that uses HTTP Cookies to post greetings as a logged-in user, it would allow a third party to update data associated with that user, making it highly dangerous from the perspective of the service provider. However, since the condition for a CSRF attack to succeed is simply that an HTTP request is accepted without verifying if it originated from a legitimate website, the presence or absence of a user login is not the direct cause.

On the other hand, note that whether or not a session cookie is issued by the website is significant for CSRF prevention. This will be discussed later.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies

As an example of serious damage occurring on a website without a login function, the 2012 Japan remote control virus case involved using a CSRF vulnerability in an inquiry form to post criminal threats.

https://en.wikipedia.org/wiki/2012_Japan_remote_control_virus_case

GET Endpoints that Perform Data Updates

An attack can also succeed if an HTTP request can be triggered against a vulnerable endpoint through methods other than the form data submission using the <form> tag described in the previous section.

The most common method is clicking a URL with a query string like https://example.com/greeting?say=Hi&to=Mom in a web browser or email client, which sends the following HTTP request to example.com:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web#query

HTTP Request
GET /greeting?say=Hi&to=Mom HTTP/2.0
Host: example.com

In this case, if the /greeting endpoint is implemented to accept data updates via GET as well as POST, the attack can be executed simply by preparing a URL and getting a user to click it, without needing to deliver malicious HTML on evil.com.

Countermeasures will be discussed later, but as with POST endpoints, it is desirable to verify whether the URL or request was issued from a legitimate website.

Cases Other Than Above

One common use case not detailed in its own section is performing update operations from an SPA to a REST API endpoint. While the form submissions and resource requests mentioned so far involve page reloads in the web browser, HTTP requests made via XMLHttpRequest or the Fetch API have the following characteristics compared to form submissions:

  • No page reload occurs after the request is sent.
  • Arbitrary HTTP methods such as PUT, DELETE, or PATCH can be issued.
  • Request content can be specified flexibly. For example:
    • Adding arbitrary HTTP headers.
    • Sending JSON as a payload.
  • Certain restrictions are imposed on whether requests can be sent or responses can be read.
    • Refers to the Same-Origin Policy. Described later.

For these endpoints as well, as previously explained, there is a high possibility of a successful attack unless you verify that the HTTP request was issued from a legitimate website.

However, attacks may be deterred due to certain browser behaviors and HTTP-related specifications. While many articles introduce implementations that take these into account as countermeasures, this article will focus on fundamental prevention methods for CSRF attacks.

Before diving into the details of prevention methods, let's review some specifications related to the behavior of the web browser, which is the execution environment.
Incorporating multiple countermeasures against vulnerabilities is desirable from the perspective of defense in depth.

https://en.wikipedia.org/wiki/Defense_in_depth_(computing)

Form Data Submission

In the "Conditions for Occurrence" section, we explained POST communication from HTML forms, but the <form> tag can issue HTTP requests in the following formats by applying specific attributes.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form

  • HTTP methods (specified by method)
    • POST
    • GET
  • Content MIME types
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

One point to note is that by creating a form like the one below using the text/plain MIME type, it is possible to send a payload that is parsable as JSON.

Ideally, the content of the request should only be interpreted as JSON when Content-Type: application/json is specified, but it is recommended to check the specifications and settings of the web framework you are using.

index.html
<form
  action="https://example.com/api/greeting"
  method="POST"
  enctype="text/plain"
>
  <input name='{"say": "Go away!!!!!", "to": "Dad", "trash": "' value='"}' />
  <button>Attack</button>
</form>
HTTP Request Payload
{"say": "Go away!!!!!", "to": "Dad", "trash": "="}

Furthermore, in the context of the Same-Origin Policy discussed in the next section, HTTP requests that can be sent from an HTML form (or similar) are defined as simple requests.
This means that HTTP requests sent from an HTML form do not trigger a preflight request.

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests

This behavior is reportedly stipulated to avoid breaking backward compatibility, as HTML forms existed before the birth of the Same-Origin Policy.

https://zenn.dev/qnighy/articles/6ff23c47018380#preflightが不要なケース

Same-Origin Policy

In the web world, ensuring content security when communication occurs from a web browser to multiple domains has long been a challenge. In this context, the concept of the Same-Origin Policy (SOP) has become well-established.

It defines a situation where the protocol, port number, and hostname of a page are identical as "same-origin," and where any of these differ as "cross-origin," imposing certain restrictions on sending requests and obtaining responses.

https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

These restrictions are referred to as CORS (Cross-Origin Resource Sharing).

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Regarding CORS, especially in the context of CSRF, it is helpful to understand Preflight requests.

https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request

A preflight request is a mechanism for the API side to determine whether to allow requests from a cross-origin, particularly when a web application consists of an SPA and a backend REST API.

When an HTTP request is cross-origin and does not fall under the aforementioned simple requests, the browser issues an OPTIONS method to the endpoint. It then decides whether to send the main request based on the contents of the HTTP headers in the response generated by the backend.


Quoted from https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Since this series of actions is performed transparently by the web browser from the perspective of the application code, as a result, it can be said that CSRF attacks usually do not succeed with HTTP requests that do not fall under simple requests, such as those listed below:

  • Requests using HTTP methods like PUT / PATCH / DELETE
  • Requests to endpoints that only accept processing when the Content-Type: application/json header is specified
  • Requests to endpoints that only accept processing when specific fixed headers, such as the X-Requested-With: XMLHttpRequest header, are set

However, this behavior assumes that settings to allow cross-domain communication are not configured on the backend side (e.g., it does not return headers like Access-Control-Allow-Origin: *). Therefore, it is preferable to choose the methods described later as fundamental countermeasures.

Another point to note is that for HTTP requests sent without a preflight request as a simple request, a CSRF attack can still succeed even if reading the response fails due to CORS restrictions.


Example of failure at the preflight request stage (top) and example of failure in reading the response after the preflight request passed (bottom)

The message when a preflight request occurs and fails is as follows (bold added by the author):
In this case, only the OPTIONS request has occurred, and it can be said that the actual request to complete the CSRF has not occurred.

Access to fetch at '.....' from origin '.....' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

The message when the preflight request passes but an error occurs at the stage of reading the response in the browser is as follows:
In this case, the request has actually occurred, so there is a high possibility that the attack has succeeded regardless of whether the response can be read by the browser.

Access to fetch at '.....' from origin '.....' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Note that the specification for preflight requests is specific to web browsers. For requests originating from environments other than web browsers, such as the curl command on a CLI or the use of the Requests module in Python, basically no such restrictions are imposed.

SameSite Attribute of Cookies

In recent years, the SameSite attribute of HTTP Cookies was introduced to mitigate CSRF.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite

When a website sets a cookie using the Set-Cookie header, specifying the following attributes prevents the cookie from being sent to the destination in cross-site (note that this is not cross-origin for historical reasons) communications:

  • In the case of SameSite=Lax:
    • Cookies are only sent for top-level navigation (URL transitions in the web browser) where the HTTP method is one of GET / HEAD / OPTIONS / TRACE.
  • In the case of SameSite=Strict:
    • Cookies are not sent under any circumstances.
  • In the case of SameSite=None:
    • Cookies are sent under all circumstances.

In general websites, to protect against CSRF attacks while maintaining conventional behavior, it is desirable to specify SameSite=Lax for session cookies.

With this behavior, while cookies are sent when visiting a site via a URL transition from an external site (e.g., via an <a> tag), session cookies are not attached to POST requests from other sites that could pose a risk of CSRF attacks. This causes most accesses to endpoints requiring login to fail.

However, as explained earlier, CSRF attacks are not directly caused by whether or not a user is logged in, but succeed if the HTTP request is not verified to have originated from a legitimate website (see the case of the 2012 Japan remote control virus case mentioned earlier). Therefore, it is preferable to implement the fundamental measures described later.

https://en.wikipedia.org/wiki/2012_Japan_remote_control_virus_case

By the way, as of 2022, the default behavior in some browsers when the SameSite attribute is unspecified has reportedly changed to Lax, improving the safety of existing sites that have not taken measures. Other complex behaviors such as the "2-minute rule" also exist; for more details, see below.

https://blog.tokumaru.org/2022/01/impact-conditions-for-no-CSRF-protection-sites.html

For more detailed explanations regarding SameSite Cookies, see below.

https://blog.jxck.io/entries/2018-10-26/same-site-cookie.html

Design and Implementation for Prevention

Application designs required to prevent CSRF are introduced in detail in the OWASP Cheat Sheet Series.

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html

In this article, I will explain two types of methods introduced under Token Based Mitigation.

Synchronizer Token Pattern

As explained so far, to prevent CSRF attacks, it is necessary to verify whether the HTTP request was issued from a legitimate website.

For example, consider a bank website https://www.abcbank.com. If a GET request to the /transferfund endpoint returns an HTML form, and a POST request to the /transfer endpoint performs a greeting post, the nature of a CSRF attack is to bypass /transferfund, which a legitimate user would always pass through, and issue a request directly to /transfer.

In other words, when the /transferfund endpoint is accessed, if you assign a session cookie to the user, issue a CSRF verification token, return it in the HTML response, and ensure that all subsequent HTTP requests include this token while confirming it matches on the server side, you can verify that the request was issued from a legitimate website.

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#synchronizer-token-pattern

An attacker cannot know the contents of the token obtained when the target user accesses the /transferfund endpoint (if the attacker themselves accesses the site, a different session ID and token from the target user will be returned), so they cannot successfully execute a CSRF attack.

The processing flow is as follows:


Quoted from https://faun.pub/cross-site-request-forgery-protection-using-synchronizer-token-pattern-72b246ded56c

When implementing, keep the following in mind:

  • If the web framework you are using provides a CSRF protection mechanism, it is better to use that rather than creating a custom implementation.
    • If you implement this mechanism yourself, you need to generate tokens in a cryptographically secure manner and perform token comparisons in a way that is resistant to timing attacks. This is not recommended as it carries a high risk of introducing new vulnerabilities.
  • Do not transmit CSRF verification tokens via cookies from the web browser.
    • The corresponding OWASP page mentions that CSRF tokens should not be transmitted using cookies.
    • This is likely because using cookies for transmission would send the token with the same lifecycle as the session cookie, defeating the purpose of verification—though it might be acceptable if SameSite=Strict is specified...?

A similar approach can be taken for REST APIs: the client-side JavaScript can obtain the token set in the HTML and include it in the HTTP request headers or payload when sending it to the server side.

For more detailed implementation, you should refer to the documentation of the web application framework you are using. At this time, you need to be aware of whether the method adopted by that framework is the Synchronizer Token Pattern or the Double Submit Cookie Pattern.

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#javascript-guidance-for-auto-inclusion-of-csrf-tokens-as-an-ajax-request-header

By the way, if this method is designed to rotate the CSRF token per HTTP request instead of per session, it can also be used as a function to prevent duplicate form submissions (problems where the Submit button is clicked repeatedly or Submit is performed consecutively in multiple tabs).

In TERASOLUNA, developed by NTT DATA, it was named Transaction Token Check.

https://terasolunaorg.github.io/guideline/public_review/ArchitectureInDetail/DoubleSubmitProtection.html#doublesubmit-how-to-use-transaction-token-check

While documentation generally recommends using the Synchronizer Token Pattern, this requires managing the CSRF verification token on the server-side by linking it to a session cookie.

If you want to implement CSRF protection without this management, the Double Submit Cookie Pattern can be used.

In this pattern, the CSRF verification token is set in a cookie, delegating its return and management to the web browser. Upon request, the token is retrieved via a delivery channel other than the cookie (such as HTTP request headers or the payload), and the token value in the HTTP request is compared with the token value in the cookie on the server-side to confirm that the request was sent from a legitimate site.

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie

The processing flow is as follows:


Quoted from https://medium.com/@kaviru.mihisara/double-submit-cookie-pattern-820fc97e51f2

Since the Double Submit Cookie Pattern stores the CSRF token—which was managed in the session in the Synchronizer Token Pattern—in a cookie on the web browser, it may seem more dangerous at first glance. Let's think a bit about why this implementation is acceptable.

From the previous explanation, we know that a CSRF verification token should have the following properties:

  1. It must be a value that an attacker cannot know or guess.
  2. It must be a value linked to the session ID.
  3. It must not be sent to the backend during cross-site access.

Regarding point 1, attackers cannot know the user's session cookie unless there is a vulnerability other than CSRF.
Regarding point 2, there is no issue because it is generated and transmitted with the same lifecycle as the session cookie.
Regarding point 3, although one of the values being compared is delivered via a cookie, the verification can still be performed because the other value is sent through a different channel. As a result, it fulfills requirements equivalent to the Synchronizer Token Pattern.

One point of caution is that cookies can be overwritten depending on the situation. Therefore, if an attacker can force a cookie with an arbitrary CSRF verification token onto a user through some method, the attack can succeed.

For example, as shown in the following article, in cases where an attacker is present in the communication path, the site cannot be protected from the attacker forcing an arbitrary cookie value. Because the Double Submit Cookie architecture assumes that communication content is not tampered with, it has unique attack risks when compared to the Synchronizer Token Pattern.

https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html

Countermeasures for this are described in the documentation: by including user-context-specific values when issuing the CSRF token on the server-side, encrypting it for delivery, and then decrypting the token sent from the web browser before comparison, tampering can be detected. In this way, even if an attacker manages to force an arbitrary verification token value, it will not match the user-context-specific value after decryption, thereby deterring the attack.

To enhance the security of this solution include the token in an encrypted cookie - other than the authentication cookie (since they are often shared within subdomains) - and then at the server side match it (after decrypting the encrypted cookie) with the token in hidden form field or parameter/header for AJAX calls. This works because a sub domain has no way to over-write an properly crafted encrypted cookie without the necessary information such as encryption key.

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie

For example, Django performs CSRF protection based on the Double Submit Cookie pattern by default, but it includes an implementation that accounts for the aforementioned issues.

https://docs.djangoproject.com/en/4.1/ref/csrf/#frequently-asked-questions

https://ops.jig-saw.com/tech-cate/django-react

Other Mitigation Methods

As mentioned in the following sections, it is worth looking into if you are interested, but it is basically recommended to take one of the aforementioned measures:

  • Use of the SameSite attribute of cookies
  • Comparison of the Origin header and the server-side origin
  • Checking custom request headers
  • Requiring user interaction before the request

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#defense-in-depth-techniques

Handling Specific Cases

Now that we have understood the concepts of the countermeasures, let's consider more realistic cases.

When SPA is Delivered as Static Content

A common configuration in recent web applications is creating a frontend application as an SPA and delivering it as a static site. In such cases, the CSRF verification token might not be present in the HTML during the initial page load.

In this situation, you can receive the CSRF token within the HTTP response of an endpoint used for retrieving the login state during the initial page display and use it for subsequent requests.

Furthermore, cookies issued during Ajax communication are automatically sent in subsequent requests. Therefore, when using the Double Submit Cookie Pattern, you can check the contents of document.cookie for each request and send the token.

https://computer-technology.hateblo.jp/entry/20131226/p1

Before Website Login

Due to the introduction of the SameSite attribute and changes in default browser behavior explained in previous sections, it has become difficult to execute CSRF attacks after login (≒ after session cookie issuance) as of 2022.

Actually, the risk of CSRF attacks is relatively increasing for pages that do not necessarily require login. For such cases, it is recommended to issue a pre-session (a session cookie issued before authentication is completed). Many web frameworks, such as Django, already consider this by default as "anonymous sessions."

https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#login-csrf

For instance, in the flow introduced in the "Double Submit Cookie Pattern" section, the session cookie and CSRF token are issued when the POST to /login succeeds. From the perspective of CSRF suppression, it is more desirable to do this at the time of the GET request for the /login page.

However, in that case, to avoid Session Fixation Attacks, the session ID should be reissued upon login.


Quoted from https://medium.com/@kaviru.mihisara/double-submit-cookie-pattern-820fc97e51f2

As a CSRF countermeasure for inquiry pages that have no concept of login, the risk can be mitigated by introducing mechanisms such as CAPTCHA, which can verify on the server side that the user performed a specific action in the web browser.

https://turningp.jp/network_and_security/public_form-csrf

Side-effect GET Endpoints

In principle, no update operations should be performed on the backend for GET endpoints.

However, there are cases where one might want to use GET endpoints for update operations for user convenience, such as Email Address Activation or Logout. If you implement these, you must be aware of the risks.

For email address activation, it is advisable to generate an authentication code that expires after a certain period and is unpredictable by third parties, include it as part of the URL, and verify the value when the request is received.

https://colo-ri.jp/develop/2012/07/web-how-to-mail-activation.html

Note that, although unrelated to CSRF attacks, some browsers and email clients perform URL Prefetching to improve processing speed. Therefore, when implementing, it is better to design the system so that the request does not hit the backend directly upon URL access, but rather uses JavaScript to send a request to the intended endpoint during the initial page load.

In this case, making the endpoint POST allows for CSRF token verification, making the implementation safer.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ

Regarding logout, it is common to log out immediately upon accessing an endpoint like /logout via GET, but it is better to create a separate endpoint that accepts POST and check the CSRF token there.

https://webcache.googleusercontent.com/search?q=cache:SQpwgKa4MqoJ:https://www.websec-room.com/2017/01/07/2727&cd=1&hl=ja&ct=clnk&gl=jp

JWT Authentication

In applications that perform session management using JWT, CSRF vulnerabilities can be suppressed if delivery methods other than cookies, such as the Authorization HTTP header, are used.

Details can be found in the following article:

https://qiita.com/mejileben/items/ae7b346f0fc4a59101b5

Summary

Here is the summary:

  • Browser behavior changes with the times, so let's introduce fundamental countermeasures like the Synchronizer Token Pattern or Double Submit Cookie Pattern 🐂
  • Don't implement it yourself; use the features provided by your web application framework 🌴
  • Understand that this is a vulnerability that can occur even for users who are not logged in 👺

Reference Materials

These are articles I referred to, although they weren't explicitly mentioned in the text 🙏

https://developer.mozilla.org/en-US/docs/Web/Security/Types_of_attacks#cross-site_request_forgery_csrf

https://utkusen.medium.com/the-state-of-csrf-vulnerability-in-2022-3858e6d90ab9

https://www.ipa.go.jp/security/vuln/websecurity.html

https://qiita.com/mpyw/items/0595f07736cfa5b1f50c

https://numb86-tech.hatenablog.com/entry/2019/02/13/221458

https://stackoverflow.com/questions/48002861/whats-the-relationship-between-csrfmiddlewaretoken-and-csrftoken

https://blog.tokumaru.org/2018/11/csrf_26.html

Discussion

ockeghemockeghem

Cross-Site Request Forgery Prevention Cheat Sheetは読むたびに内容が変更されているように思います。内容が安定しておらず、信頼性が低いと考えられます。
Double Submit Cookieについては、以前以下の記事で内容を批判しました。

https://blog.tokumaru.org/2018/11/csrf_26.html

これを読んでいただけるとわかると思いますが、Double Submit Cookieについては推進派と反対派が論争をしていて編集合戦の様相を呈しています。私は反対派です。

Cookieの改変については以下の記事および動画で解説をしています。

https://blog.tokumaru.org/2013/09/cookie-manipulation-is-possible-even-on-ssl.html

https://www.youtube.com/watch?v=GP1eEit1quY

サーバーサイドでのトークン発行時に値を暗号化して配送した上で、Webブラウザから送られてきたトークンを復号化してから比較すれば、改ざんを検知することができる

これは文字通り解釈すると間違いでしょう。攻撃者は自分用のトークンCookieを入手して、それを被害者のブラウザにセットしてやれば改ざんすることなく悪用が可能です。トークンにユーザIDなどを含める等しなければ、CSRF攻撃は検知できないと思います。

JWTはセッションCookieと同等の役割をする上、仕様上改ざん検知をおこなうことができるため、Cookieを用いたCSRFチェック全般が不要になる

JWTをCookieで受け渡しするサイトもあり、この場合はCSRF脆弱になる可能性があります。JWTだからCSRFセーフというわけではなく、トークンをリクエストヘッダで送信するからCSRF脆弱性の余地がないだけで、改ざん検知は関係ないと思います。

Yuuki TakahashiYuuki Takahashi

ご指摘および詳細に解説を頂きましてありがとうございます🙏
数日中に以下の方向で加筆・修正したいと思います

Double Submit Cookieについて

解答:CSRFの防止策に関するチートシートにツッコミを入れる はじめ頂いたリンクについて拝読致しました
OWASPのDouble Submit Cookieの説明を読んで漠然と不安を感じていたのですが、危険性のある具体的なケースを例示頂けたことでイメージが湧き大変ありがたかったです

一方で、(徳丸さんの他の記事でも紹介されていますように、)いくつかの著名なWebフレームワークでも現在採用されている手法であることと、本記事はアプリケーション開発者に向けた記事であり、利用しているWebフレームワークが採用している実装をそのまま利用することを推奨する趣旨であることから、Double Submit Cookie を使ってはならない…といった立場は取らないつもりで考えています

つきましては、以下について補足したいと思います

  • 注意点としては、Cookie は状況によって上書きすることができるため、攻撃者が何かしらの手法でユーザーに対して~何かしらの手法 について HTTPSを使ってもCookieの改変は防げないことを実験で試してみた の記事のケースを紹介し、その場合において Double Submit Cookie は Synchronizer Token Pattern と比較した際に固有の攻撃リスクがある
    • が、一般的にはWebアプリケーションフレームワーク側でそうした問題への考慮がおこなわれているため、利用者としてはまずは各フレームワークのドキュメントを読んで適切な実装をおこなうのが重要である
  • Webサイトが他の脆弱性を含む際にリスクが増加する点については、HTTPヘッダインジェクションなどDouble Submit Cookie特有のリスクとなり得るものについて紹介する
  • クッキーモンスターバグについては近年のブラウザ環境では解消されているため記事中では紹介しない

こちらについては、ご指摘頂いている トークンにユーザIDなどを含める等しなければ のニュアンスが漏れてしまっていることが問題であり、その旨を加筆する必要があると理解しました
検証用トークンについて、Cookieで配送する以上は外部から特定値を強制されるリスクがあるため、そのユーザーのコンテキスト(≒セッション)固有に発行されたものであることが復号化時に判断可能であることが必須要件である…という記載が必要であると考えています

JWTについて

私がJWTについて本番環境での利用経験がなく、特に認識が曖昧なまま書いてしまっていたものと思います
JWTをCSRF対策に活用できるのは配送経路にHTTPヘッダ等を利用した(Cookieを用いなかった)場合に限られることと、改ざん検知の下りは記載を削除します
(これについては、そもそも参考として貼り付けていた記事自体にもそのような注意喚起がされていたものでしたので、杜撰な記述をしてしまっていました…今後気をつけたいと思います)