Unauthorized Cross-Site Request Forgery (CSRF)

Severity: Low
Test name: Cross-Site Request Forgery (CSRF)

Unauthorized Cross-Site Request Forgery (CSRF) exploits occur when an attacker tricks a user's web browser into executing an unwelcome action on a trusted site where the user is logged in. This type of attack is possible because browsers automatically append all cookies, including session cookies, to every request made to a site. Consequently, if a user is authenticated, the website cannot differentiate between authentic requests and fraudulently constructed requests.

Attackers have various methods to entice users to trigger these malicious requests, such as:

  • Sending an email containing a link that initiates a harmful request.
  • Embedding a 0x0 pixel "invisible" image in an email, where the image source triggers the malicious request.
  • Creating a deceptive web application with a form designed to launch a malicious request either automatically upon page load (<body onload="document.forms[0].submit()">) or through a user's click on a submit button.
  • Crafting a fake web application that executes a malicious "XMLHttpRequest" through embedded JavaScript.

The CSRF attack may be executed for the following purposes:

  • Send money from one account to another
  • Change a user's password or a secret question
  • Gain administrative privileges
  • Purchase with the user's credentials
  1. The user is using a bank website (https://{your-bank}.com) and has an active session in his browser (for example, this website is opened in one of the browser tabs).
  2. An attacker creates a special link to transfer money between two accounts:
  1. The attacker sends this link to the user in any possible way. For example, the attacker can use an email to send the link that will be shown for the user as a “broken" picture. The body of the email should contain:
<img src="https://{your-bank}.com/transfer?from=account1&to=account2&amount=1000/>
  1. When the user clicks this image, the corresponding URL opens in a new browser tab. As the user still has an active session (see point 1), the money from "account1" will be transferred to "account2". When the user is authenticated, an unwanted action is performed on a trusted site.
  • The issue can be found in the source code on the server side.
  • The issue can be found in the server configuration.
Remedy suggestions
  • Check if your framework has built-in CSRF protection and use it.
  • Always use the "SameSite" Cookie Attribute for session cookies. Based on your application use cases, "Lax" or "Strict" value should be used.
Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict
Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax
  • Do not use the GET method for state-changing requests. The GET request should be used only for retrieving the information.

  • If possible, try to avoid cross-site requests. Modern web browsers support same-origin policy restrictions, so do not configure CORS headers on your server.

  • If your application supports cross-site requests, carefully configure CORS headers. In the "Access-Control-Allow-Origin" header, configure allowed domain names.

    • Nginx:
    if ($http_origin ~* (whitelist\.address\.one|whitelist\.address\.two)$) {
    add_header Access-Control-Allow-Origin "$http_origin";
    • Apache:
    <IfModule mod_headers.c>
        SetEnvIfNoCase Origin "https://(whitelist\.address\.one|whitelist\.address\.two)$" 
        Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e 
    • IIS 7.5+:
            <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
            <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS,PUT,DELETE" />
                    <clear />                
                    <rule name="AddCrossDomainHeader">
                        <match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
                        <conditions logicalGrouping="MatchAll" trackAllCaptures="true">
                            <add input="{HTTP_ORIGIN}" pattern="(https://(whitelist\.address\.one|whitelist\.address\.two))" />
                        <action type="Rewrite" value="{C:0}" />
    • If the cookie is used for storing session information, then the cookie has to be "HTTP-only" and "Secured" one:
    Set-Cookie: sessionId=some_session_hash; Expires=Thu, 21 Oct 2021 07:28:00 GMT; Secure; HttpOnly
  • CWE-352
  • CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N