Skip to content

Cross-site request forgery (CSRF)

There is common confusion about the difference between XSS (Cross-site scripting) and CSRF (Cross-site request forgery). 

In performing Cross-site scripting (XSS) attack, an attacker can execute a script within the browser of a victim user. While performing a Cross-site request forgery (CSRF) attack, an attacker can induce a victim user to perform actions they do not intend to.

These are the steps the attacker will perform to hijack the victim’s identity:

  • Persuade a victim with social engineering techniques to click on the link sent in the mail, which will trigger a request to the targeted site. 
  • After going to some website malicious request is triggered, the application will assume that it comes from a signed user. An attacker will perform a requested action without the user’s knowledge or consent.

Purpose of CSRF attack

Target is often to change server state and/or to gain access to sensitive data. When a CSRF attack is successful against the victim’s account, the attacker has the option to transfer funds, purchase a product, modify account settings, or any other action that the signed user could perform.

Example

As I mentioned, the attacker will try to persuade the targeted user to click on the link that will

send the request.

If the attackers were messing with DOM elements, they would use the href tag:

<a href="https://someUrlWithSomeParameters.com">Click for more details</a>

If the application has some filter restriction on navigation, and if the application is using POST requests attacker can try to use the form tag. They would create a form and, with JS, submit the POST request.

I found one simple example of adding a form on site Portswigger, and the code is below.

This example is the hidden HTML form that will, without user knowledge, create a post request. If the user was redirected to another site, this site could contain this hidden form to hijack the user identity.

Code in script tag will trigger submit of the form and send the request.

<html>

    <body>

        <form action="https://vulnerable-website.com/email/change" method="POST">

            <input type="hidden" name="email" value="pwned@evil-user.net" />

        </form>

        <script>

            document.forms[0].submit();

        </script>

    </body>

</html>

What are CSRF Tokens

CSRF tokens are used to protect from CSRF attacks. Tokens are unique and are created as a secret value generated by the server and sent to the client to be included in subsequent HTTP requests created by the client. 

How we prevent CSRF attacks using CSRF tokens

When the HTTP request is made, the server-side validates the request, including the expected token. If the token is missing or invalid, validation will not pass, and the request will be rejected.

So, when an attacker creates a request, it will not pass because the requests will not have all the required parameters. After all, the attacker can not guess the value of the CSRF token.

Tokens should be generated as session tokens should be. They should be unpredictable, PRNG (pseudo-random number generator) should be used, etc.

Where are CSRF tokens transmitted?

Synchronized token pattern

There is an approach to transmit the token in a hidden HTML form field. This approach is often called the Synchronized token pattern. This pattern presents the flow of setting a unique, valid token for each HTTP request and then checking that value when the HTTP request is subsequently sent. It is done by setting hidden fields.

Example

The token would then be added in post request on form submit. We can create a hidden input field with the value which is the value of the CSRF token, something like this:

<input type="hidden" name="token" value="valueOfCSRFToken" />

It is very important that the hidden field is loaded before any non-hidden fields in HTML, so the attacker cannot catch the field’s content.

The problem with using this approach is that hidden token needs to be implemented on all HTML forms in the application. Also, you would need to keep track of the valid tokens from the server side and check out each request if it is using a valid token. 

There is also a possibility to send CSRF tokens in a custom request header using a cookie-to-header token pattern.

Cookie-to-header token pattern

This is the second anti-CSRF technique. The cookie should be set once in the session. After JS reads the cookie’s content, a custom HTTP header should be set (X-CSRF-TOKEN or X-XSRF-TOKEN or XSRF-TOKEN) with that value from the content. Each request will send a header with the mentioned token (from custom HTTP header) and the cookie (from standard HTTP header), so the server can check if those two values match.

Now we can be confused if we don’t figure out the point of this approach. But no worries, it is not so clear without knowing its exact idea. The purpose of it is that only JavaScript, which runs on the same domain as the cookie domain, would have access. So, only JS with the same domain can set up the correct value of the cookie’s content to the custom HTTP header.

This approach would only work with JavaScript requests (XHR requests). So, requests that would be set up by HTML form would not set the header. 

Example (JavaScript):

The web application is using session, and it sets session cookies. You can check out the Session Management article for more information regarding the sessions.

In the HTTP response header, Set-Cookie will be visible if HTTP is used to send a cookie from the server to the client side.

Content would be token, maximum age, expiration date, and cookie attribute, and it would look something like this:

Set-Cookie: __Host-token=RANDOM; Expires=Mon, 01-Jan-2023 12:55:00 GMT; Max-Age=31449600; Path=/; SameSite=Lax; Secure

It is essential to use cookie prefixes for a cookie with CSRF token value. This means that it can not be overwritten from another subdomain. The path should be “/.” And as you saw, it is marked as Secure, meaning it can not be sent over an unencrypted HTTP request. SameSite attribute helps the browser to decide if it should send cookies with cross-site requests. In this case, Lax is used; he is the default attribute. If you want to learn more regarding the SameSite attribute, check out this site.

When JavaScript (client side) reads a cookie, copy it to a custom HTTP header sent with each request. 

That would look something like this:

X-Csrf-Token: RANDOM

After, only validation of the token is left.

* The CSRF token cookie mustn’t have the httpOnly cookie flag! HttpOnly flag is used against XSS vulnerability because it makes the value of the cookie unavailable from JavaScript. So, you can conclude from the explanation that JavaScript will be prevented from reading X-Csrf-Token.

Double submit cookie pattern

There is the third pattern as the variation of the two mentioned patterns. This one puts the X-Csrf-Token value in a hidden form field but keeps the server-side logic simpler than the first pattern- The Synchronizer token pattern. This approach has some weaknesses, and you can check them on site.

Enabling CSRF in Angular

There is available Angular documentation for CSRF protection. 

First, you need to configure cookie/header names. To do that, you will need to import both HttpClientModule and HttpClientXsrfModule. In theory, it is easier to sync the names in the backend and frontend, but if you have different names on the server side, you can use the withOptions() method explained on this site.

  imports: [

      HttpClientModule,

      HttpClientXsrfModule.withOptions({

        cookieName: 'X-Csrf-Cookie',

        headerName: 'X-Csrf-Header',

      }),

    ]

In the mentioned documentation, you can find code that will help you test if your implementation is correct. 

Now we are left with intercepting requests and checking if the token in the cookie is the same value as it is in the HTTP request. In the code below, a custom interceptor class is created, which uses HttpXsrfTokenExtractor to get the token value so it can be compared.

@Injectable()

export class CustomHttpInterceptor implements HttpInterceptor {

    constructor(private spinnerService: SpinnerService, private toastrService: ToastrService,

        private httpXsrfTokenExtractor: HttpXsrfTokenExtractor) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const cookieheaderToken = 'X-XSRF-TOKEN';

        let csrfToken = this.httpXsrfTokenExtractor.getToken();

        if (csrfToken !== null && !req.headers.has(cookieheaderToken)) {

            req = req.clone({ headers: req.headers.set(cookieheaderToken, csrfToken) });

        }

        this.spinnerService.show();

        return next.handle(req)

            .pipe(tap((event: HttpEvent<any>) => {

                if (event instanceof HttpResponse) {

                    this.spinnerService.hide();

                }

            }, (error) => {

                if (error.status == 400) {

                    this.toastrService.error("Warning", error.error.Error)

                } else if (error.status == 500) {

                    this.toastrService.error("System error occurred");

                }

                this.spinnerService.hide();

            }));

    }

}

Conclusion

 

I hope I succeeded with a simple explanation of this type of attack. If you want to further investigate the prevention or even how the attack is performed, there is plenty of literature on the Internet. I found a lot of GitHub repositories with app samples for this attack. I think it is very good to check them out, but if you want to clone them, make sure it does not contain malicious code!

OWASP is constantly upgrading its Prevention Cheat Sheet for the Cross-Site Request Forgery, so you should check that one also.

Good luck with your investigation!

In the end, secure code is the cheapest code!

Cover photo by Philipp Katzenberger

#CSRF #cookie_to_header_token_pattern #synchronized_token_pattern

About Version 2 Digital

Version 2 Digital is one of the most dynamic IT companies in Asia. The company distributes a wide range of IT products across various areas including cyber security, cloud, data protection, end points, infrastructures, system monitoring, storage, networking, business productivity and communication products.

Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, different vertical industries, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

×

Hello!

Click one of our contacts below to chat on WhatsApp

×