Airing

Airing

哲学系学生 / 小学教师 / 程序员,个人网站: ursb.me
github
email
zhihu
medium
tg_channel
twitter_id

CSRF Attack Methods and Defenses

Types of CSRF Attacks#

CSRF, short for Cross Site Request Forgery, is also known as XSRF, one-click attack, or session riding. It is a type of attack that hijacks a trusted user's request to the server, sending unexpected requests. The attacker induces the victim to visit a third-party website, which then sends cross-site requests to the targeted website. By leveraging the victim's already acquired registration credentials on the targeted website, the attacker can bypass backend user verification and impersonate the user to perform certain actions on the targeted site. Compared to XSS, XSS exploits the user's trust in a specific website, while CSRF exploits the website's trust in the user's web browser.

Typically, CSRF attacks occur when the attacker uses the victim's cookies to deceive the server into trusting them, allowing requests to be forged in the victim's name without their knowledge, thus executing operations that are under permission protection without authorization.

CSRF Principle

In a response from Li Xiangtian on Zhihu, there is a vivid description about CSRF:

Anti-theft system activated:
Mom: Keep an eye on the clothes for me.
Child: Okay.

The thief arrives
Normal operation:
Child: Who are you?
Thief: I am Zhang San.
Child: Mom, someone is stealing the clothes.
Mom: Who?
Child: Zhang San.

The thief is caught
Vulnerability:
Child: Who are you?
Thief: I’m just playing with you.
Child: Mom, someone is stealing the clothes!
Mom: Who?
Child: Just playing with you.
Mom: ...

CSRF allows a user to unknowingly initiate a request impersonating their identity:
Thief: Your mom is calling you to buy laundry detergent.

The principle of CSRF is quite simple, even appearing more monotonous compared to XSS. Specifically, it includes the following three types of attack:

  1. GET type CSRF
  2. POST type CSRF
  3. Link type CSRF

GET Type CSRF#

This type of attack is very simple and only requires one HTTP request:

 <img src="http://a.com/withdraw?amount=10000&for=hacker" > 

When the victim visits a page containing this img, the browser will automatically send an HTTP request to a.com. a.com will receive a cross-domain request containing the victim's login information.

POST Type CSRF#

This type of CSRF typically uses an automatically submitted form, such as:

 <form action="http://a.com/withdraw" method=POST>
    <input type="hidden" name="account" value="airing" />
    <input type="hidden" name="amount" value="10000" />
    <input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script> 

After accessing this page, the form will automatically submit, simulating a user completing a POST operation. This type of CSRF is similar to the first one, as both simulate requests, so the backend interface cannot rely solely on allowing POST requests for security.

Link type CSRF is not common; compared to the other two types where users get caught just by opening a page, this one requires the user to click a link to trigger it. However, it is essentially the same as the first two. This type usually embeds malicious links in images posted on forums or entices users to click through advertisements, often using exaggerated language to trick users, such as:

 <a href="http://a.com/withdraw.php?amount=1000&for=hacker" taget="_blank">
 The Dragon-Slaying Sword, click to receive! 
 <a/>

Since the user has previously logged into a trusted website A and saved the login state, as long as the user actively visits the above page, the attack is successful.

CSRF Defense Methods#

CSRF usually originates from third-party websites, and the targeted website cannot prevent the attack from occurring; it can only enhance its own protection against CSRF to improve security.

The above text mentions two characteristics of CSRF:

  1. CSRF (usually) occurs on third-party domains.
  2. CSRF attackers cannot obtain cookies or other information, they can only use them.

Based on these characteristics, the following two defense strategies can be formulated for CSRF:

  1. Automatic Defense: Block access from unknown external domains
  • Same-origin detection
  • Samesite Cookie
  1. Active Defense: Require additional information that can only be obtained from the same domain during submission
  • Synchronizer Tokens
  • Double Cookie Defense
  • Custom Header

Automatic defense utilizes the inherent characteristics of the HTTP protocol for automatic protection, while active defense requires programming measures for defense.

CSRF Automatic Defense Strategy#

Same-Origin Detection#

Since CSRF mostly comes from third-party websites, we can directly prohibit external/untrusted domains from making requests to us.

In the HTTP protocol, each asynchronous request carries two headers to mark the source domain:

  • Origin Header<□ />
  • Referer Header<□ />

By verifying whether these two headers are trusted, same-origin detection can be achieved. However, this method is not foolproof; the value of the Referer is provided by the browser, and although the HTTP protocol has clear requirements, each browser may implement Referer differently, which does not guarantee that the browser itself is free from security vulnerabilities. Using the method of verifying the Referer value relies on third parties for security, which is theoretically not very safe. In some cases, attackers can hide or even modify their request's Referer. When writing crawlers, we often modify headers to bypass the server's same-origin detection. In the article 【Basic Skills】 Frontend Security Series Part Two: How to Prevent CSRF Attacks? | Meituan Technical Team, the credibility of the Referer and dangerous scenarios are analyzed in detail, which will not be elaborated here due to space limitations.

In summary, same-origin verification is a relatively simple preventive method that can prevent the vast majority of CSRF attacks. However, it is not foolproof; for websites with high security requirements or a lot of user input, we need to implement additional protective measures for critical interfaces, which will be discussed in the active defense strategy below.

To address this issue at its source, Google drafted a proposal to improve the HTTP protocol, which is to add a Samesite attribute to the Set-Cookie response header, indicating that this cookie is a "same-site cookie" that can only be used as a first-party cookie and not as a third-party cookie. SameSite has two attribute values: Strict and Lax.

  • Samesite=Strict: Strict mode, indicating that this cookie can never be used as a third-party cookie under any circumstances, without exception.
  • Samesite=Lax: Lax mode, which relaxes some restrictions compared to Strict. If this request is a synchronous request (changing the current page or opening a new page) and is also a GET request, then this cookie can be used as a third-party cookie.

However, Samesite Cookies also have some issues:

  1. The compatibility of Samesite is not very good; currently, apart from support from the latest versions of Chrome and Firefox, Safari and iOS Safari do not support it, and it cannot be widely adopted at this stage.
  2. Moreover, Samesite Cookies currently have a fatal flaw: they do not support subdomains. For example, a cookie set under blog.ursb.me cannot be used by a Samesite Cookie set under ursb.me. This means that when our website has multiple subdomains, we cannot use Samesite Cookies to store user login information on the main domain. Each subdomain requires the user to log in again. This is impractical.

CSRF Active Defense Strategy#

CSRF active defense measures include the following three:

  1. Synchronizer Tokens: Render the token onto the page when responding, and submit it through a hidden field during form submission.
  2. Double Cookie Defense: Set the token in a cookie, submit the cookie during POST requests, and include the token from the cookie in the header or body for server-side comparison and verification.
  3. Custom Header: Trust requests that carry a specific header (e.g., X-Requested-With: XMLHttpRequest). This scheme can be bypassed, so frameworks like Rails and Django have abandoned this defense method.

Therefore, the following will mainly discuss the first two defense methods.

Synchronizer Token#

Synchronizer Token, or synchronized form CSRF verification. The reason CSRF attacks can succeed is that the server mistakenly treats the request sent by the attacker as a request from the user. Therefore, we can require all user requests to carry a CSRF token that the attacker cannot obtain. The server distinguishes normal requests from attack requests by verifying whether the request carries the correct token, thus preventing CSRF attacks.

Specifically, it involves the following three steps:

  1. Output the CSRF Token to the page.
  2. The request submitted by the page carries this Token, usually hidden in the form field as a parameter or appended to the URL as a query.
  3. The server verifies whether the Token is correct.

When the user obtains the Token from the client and submits it to the server again, the server needs to determine the validity of the Token. The verification process involves decrypting the Token, comparing the encrypted string and timestamp; if the encrypted string matches and the time has not expired, then this Token is valid. The value of such a Token is usually generated using UserID, timestamp, and random numbers through encryption. This encryption can verify the requesting user, the request time, and ensure that the Token is not easily cracked. Personally, I use the following encryption method in projects for reference:

import md5 from 'md5'

export const MESSAGE = {
  OK: {
    code: 0,
    message: 'Request successful',
  },
  TOKEN_ERROR: {
    code: 403,
    message: 'TOKEN expired',
  },
}

const md5Pwd = (password) => {
  const salt = 'Airing_is_genius'
  return md5(md5(password + salt))
}

export const validate = (res, check, ...params) => {

  for (let param of params) {
    if (typeof param === 'undefined' || param === null) {
      return res.json(MESSAGE.PARAMETER_ERROR)
    }
  }

  if (check) {
    const uid = params[0]
    const timestamp = params[1]
    const token = params[2]

    if (token !== md5Pwd(uid.toString() + timestamp.toString() + KEY))
      return res.json(MESSAGE.TOKEN_ERROR)
  }
}

This method is safer than the previous checks for Referer or Origin; the Token can be generated and stored in the Session, and then retrieved from the Session for comparison with the Token in the request. However, there are two points to note.

  1. Session Vs Cookie: If the token can be stored in the Session, it is a good choice.
  2. Refresh CSRF Token: When the CSRF token is stored in a Cookie, if a user switches on the same browser, the newly logged-in user will still use the old token (used by the previous user), which poses a certain security risk. Therefore, the CSRF token must be refreshed every time a user logs in.

Double Cookie Defense, translated as dual cookie verification.

Storing the CSRF Token in the Session is cumbersome and cannot be uniformly handled across all interfaces. Another defense measure is to use double-submit cookies. Utilizing the characteristic that CSRF attacks cannot access user cookies, we can require Ajax and form requests to carry a value from the cookie.

  1. When the user visits the website page, inject a cookie into the request domain with a random string.
  2. When the frontend makes a request to the backend, retrieve the cookie and add it to the URL parameters.
  3. The backend interface verifies whether the field in the cookie matches the field in the URL parameters; if not, it rejects the request.

This method is much simpler compared to CSRF Tokens. It can be automated through front-end and back-end interception methods. Backend verification is also more convenient, requiring only a comparison of fields in the request, and does not need to query and store Tokens. However, it has not been widely adopted, especially on large websites, and has serious flaws. For example:

Since any cross-domain request will prevent the frontend from accessing fields in the cookie (including between subdomains), when a user visits my me.ursb.me, and my backend API is deployed on api.ursb.me, the user at me.ursb.me cannot access the cookie from api.ursb.me, thus failing the double cookie verification. Therefore, our cookies are placed under the main domain ursb.me to ensure that each subdomain can access them. However, I have also deployed many other sub-applications under ursb.me. If a subdomain xxx.ursb.me has a vulnerability, although this xxx.ursb.me may not have any valuable information to steal, an attacker can modify the cookies under ursb.me, thus executing XSS attacks and using the tampered cookies to launch CSRF attacks against me.ursb.me. Additionally, to ensure the secure transmission of cookies, it is best to ensure that this defense method uses HTTPS for the entire site; if HTTPS has not been implemented, using this method may carry risks.

The following is a summary of double token verification from 【Basic Skills】 Frontend Security Series Part Two: How to Prevent CSRF Attacks? | Meituan Technical Team:

Advantages:

  • No need to use Session, broader applicability, easier to implement.
  • Tokens are stored on the client side, which does not burden the server.
  • Compared to Tokens, implementation costs are lower, can be uniformly intercepted and verified on both front and back ends, without needing to add them to each interface and page.

Disadvantages:

  • Extra fields are added to the cookies.
  • If there are other vulnerabilities (such as XSS), attackers can inject cookies, rendering this defense ineffective.
  • Difficult to achieve isolation between subdomains.
  • To ensure the secure transmission of cookies, it is best to ensure that this defense method uses HTTPS for the entire site; if HTTPS has not been implemented, using this method may carry risks.

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.