Cross-site request forgery

From David's Wiki
\( \newcommand{\P}[]{\unicode{xB6}} \newcommand{\AA}[]{\unicode{x212B}} \newcommand{\empty}[]{\emptyset} \newcommand{\O}[]{\emptyset} \newcommand{\Alpha}[]{Α} \newcommand{\Beta}[]{Β} \newcommand{\Epsilon}[]{Ε} \newcommand{\Iota}[]{Ι} \newcommand{\Kappa}[]{Κ} \newcommand{\Rho}[]{Ρ} \newcommand{\Tau}[]{Τ} \newcommand{\Zeta}[]{Ζ} \newcommand{\Mu}[]{\unicode{x039C}} \newcommand{\Chi}[]{Χ} \newcommand{\Eta}[]{\unicode{x0397}} \newcommand{\Nu}[]{\unicode{x039D}} \newcommand{\Omicron}[]{\unicode{x039F}} \DeclareMathOperator{\sgn}{sgn} \def\oiint{\mathop{\vcenter{\mathchoice{\huge\unicode{x222F}\,}{\unicode{x222F}}{\unicode{x222F}}{\unicode{x222F}}}\,}\nolimits} \def\oiiint{\mathop{\vcenter{\mathchoice{\huge\unicode{x2230}\,}{\unicode{x2230}}{\unicode{x2230}}{\unicode{x2230}}}\,}\nolimits} \)

Cross-site request forgery, also known as CSRF or XSRF, is a attack in which an alternate website sends fake requests to a real website.

Attack

The attack is like this:

  1. A user is logged into shopping.com, an online store where he shops regularly.
    • shopping.com leaves a session id in a cookie so the user doesn't need to login every visit.
  2. The user is sent a email linking to attacker-website.com. They visit this website.
  3. The JS code in attacker-website.com sends a POST request to shopping.com to order an item.
    • This request will carry the session id which is already authenticated.
    • Without CSRF protection, the order will go through.

Defense

To protect against CSRF, the website shopping.com should do the following:

  1. While the user is browsing shopping.com, the backend sets a CORS cookie using an HTTP header in a response.
    XSRF-TOKEN:UKL7smHAK4xENQj5pYbi
    • This cookie will be different for each session.
  2. When making requests to checkout, the front-end will add the XSRF token to the HTTP request as a header or hidden form field.
    X-XSRF-TOKEN:UKL7smHAK4xENQj5pYbi
  3. Before processing the order, the backend will check the XSRF header to make sure that it matches what was sent originally.
    • I.e. it checks that the (XSRF header or hidden field) matches the (sent cookie or save session token).
Notes
  • Typically, the server will update the CSRF cookie every request.
  • If using a header, the defense is call cookie-to-header.
  • Using a hidden form field is called double submit cookie and does not require JavaScript.

Defense Explanation

This works for the following reasons:

  1. When sending the XSRF token, the backend sends it in an HTTP header telling the browser to stores it as a cookie,
    • If other websites make a request (via fetch or XmlHTTPRequest) to get an XSRF token, the token will be sent in the HTTP header.
      By default, the browser will only let the other website see a few whitelisted HTTP headers due to same-origin policy so they will not see the XSRF token.
  2. Next the browser stores the cookie under shopping.com.
    • Due to same-origin policy, other websites cannot see the cookies for shopping.com.

Note that other origins may be able to read hidden form fields and can send custom headers if Access-Control-Allow-Origin is set incorrectly.
It is important for the backend to check the XSRF key against one that other websites cannot read.

If you send the XSRF token in a cookie and a hidden form field, this will not require any JS since the browser will automatically repeat the hidden form field.
However, you need to make sure no CORS header is sent otherwise other websites will be able to read the XSRF token sent in the hidden form field. E.g.

Access-Control-Allow-Origin: https://shopping.com