What is CSRF & How to Load Test CSRF-Protected Websites
January 3, 2023

What is CSRF & How to Load Test CSRF-Protected Websites

Performance Testing

More and more sites, applications, and application frameworks are expected to start implementing protection from Cross-Site Request Forgery (CSRF) in the very near future. While this is an exciting development in terms of application security, it does present challenges when it comes to automated load testing. 

This post will take a closer look at what CSRF is and show you how to bypass CSRF protection in your JMeter test. 

Back to top

What is CSRF?

Cross-Site Request Forgery is an attack that forces the user to execute unwanted actions on a site where he is authenticated. Thanks to social engineering (malicious links sent via chat or email) or XSS vulnerabilities, the user ends up making requests he never intended to make. 

Here are a few ways that CSRF can impact end users: 

  • Logging the user out.
  • Changing their password.
  • Gaining access to a restricted resource.
  • Escalating the attacker’s privileges for a certain web application.

CSRF attacks are made through the user’s identities, such as headers, cookies, credentials, and privileges. In short, anything associated with the target user’s session - starting from IP addresses and ending with Windows Domain credentials - can be used against them.  

These dangers from CSRF are why different content management systems and frameworks like Drupal, .NET Framework, and Django have built-in protection from CSRF requests. This means that developers do not usually need to worry about implementing CSRF protection themselves. 

Back to top

CSRF & JMeter

From the end user’s point of view, CSRF protection is transparent. On the protocol level, CSRF protection is an additional mandatory dynamic parameter, such as:

  • Cookies
  • Headers
  • Request Parameters

When a real-life user surfs a CSRF-protected website with a web browser, the browser’s CSRF security token can be set (for example this can be set with a JavaScript function). Now, here’s where JMeter’s “not being a browser” issue really becomes a limitation. As it’s not a browser, it can’t execute a client-side JavaScript and therefore can’t generate and record a proper CSRF token. 

To resolve the challenges raised by CSRF sites, you will need to use a JMeter Correlation.  Here, I’m referring to the software testing definition of “correlation”: the process of handling dynamic parameters by extracting them from a previous response, storing them as variables, and adding them to the next request as a parameter. 

Back to top

Extracting the CSRF Token With JMeter Post-Processors

Let us inspect two recorded requests to a Django website to see how they are different. If you’re not comfortable recording JMeter tests, it is worth checking out one of the following guides:

  1. JMeter's Superpower: The HTTP Proxy Server
  2. Recording HTTPS Traffic with JMeter's Proxy Server
  3. Blazemeter Extension for Google Chrome

I would recommend using the BlazeMeter Chrome Plugin since you will not need to worry about proxies, SSL certificates, excluding common unwanted assets, etc.  

Attempt One:

JMeter load test with highlighted CSRF Middleware Token parameter

Attempt Two:

Second JMeter load test with highlighted CSRF Middleware Token parameter

As you can see, we need to correlate something called “csrfmiddlewaretoken”. In order to do this, we need to inspect the previous response details. The View Results Tree listener offers the easiest way to see details of requests, responses and debugging tests. 

Run the Recorded Load Test Scenario

Let us add the View Results Tree listener to our recorded test plan and see the response details of the request before login.

  1. Select the request before the actual login attempt (the failed one) in the View Results Tree listener
  2. Switch to the “Response Data” tab - as this holds the actual server response
  3. Type “csrf” into the “Search” input and click the “Find” button
View Results Tree with hidden "csrfmiddlewaretoken" parameter

Voila! We have a hidden input named “csrfmiddlewaretoken” and it looks like its value attribute is holding the dynamic CSRF token needed for a successful login. 

<input type='hidden' name='csrfmiddlewaretoken' value='sTrKh7qgnuKtuNTkbwlyCv45W2sqOaiY' />

Based on the response nature, you can use the following Post-Processor options: 

Please note that in all cases, the ‘Reference Name’ is the name of the JMeter Variable where the extractor result will be stored. This name can be anything as long as it is meaningful. For example: if you set ‘TOKEN’ as the Reference Name, you will be able to access this variable as ${TOKEN} later on in the script. 

  1. Regular Expression Extractor:
    • Apply to and Field to check: depends on where you expect the value to appear. In my case it’s “Main Sample Only” and “Body”
    • Reference Name: REGEX_TOKEN
    • Regular Expression: 

<input type='hidden' name='csrfmiddlewaretoken' value='(.+?)' />

  • Template: $1$
  1. XPath Extractor
    • If your response is not XML/XHTML compliant, check Use Tidy box
    • Reference Name: XPATH_TOKEN
    • XPath Query: 

//input[@name='csrfmiddlewaretoken']/@value

  1. CSS/JQuery Extractor
    • Reference Name: CSS_TOKEN
    • CSS/JQuery Expression: 

input[name=csrfmiddlewaretoken]

  • Attribute: value

Technically, you can use any of the above approaches.

We typically recommend using the Regular Expression Extractor whenever possible because it’s the fastest and least memory-consuming way to correlation. On the other hand, if there’s a complex markup, it might be easier to use the XPath or CSS/JQuery as Regular Expressions are fragile and sensitive to any changes (such as extra spaces, line breaks, attributes location etc.). If you are sure that the entity you are trying to extract is more or less “static,” your best bet is to use the Regular Expressions. 

But there could be an easier way as CSRF Tokens can be found in other response parts. So let’s go back to our View Results Tree listener and  take a look at the “Response Headers” section on the “Samper Result” tab for the same request.

Construct CSRF Middleware Token Request Parameter

In the following image, you will see that the “csrftoken” cookie value is exactly the same as “csrfmiddlewaretoken”. Therefore, it can be used to construct the relevant “csrfmiddlewaretoken” request parameter.

View Results Tree showing that "csrftoken" and "csrfmiddlewaretoken" are the same.

Set-Cookie: csrftoken=sTrKh7qgnuKtuNTkbwlyCv45W2sqOaiY; expires=Sun, 20-Dec-2015 11:34:43 GMT; Max-Age=31449600; Path=/

To extract this parameter from the Set-Cookie header, add a Regular Expression Extractor Post-Processor as a child of the request penultimate to the login request. Now configure it as follows:

  • Apply to: Main sample only
  • Field to check: Response Headers
  • Reference Name: CSRF_TOKEN
  • Regular Expression: Set-Cookie: csrftoken=(.+?);
  • Template: $1$

Get Response Cookie via the Regular Expression Extractor

Now is a good time to replace a hard-coded recorded “csrfmiddlewaretoken” request parameter value with the one we extracted from the “csrftoken” cookie. To do this, just use the “Reference Name” you defined in the extractor postprocessor as follows:

  • ${CSRF_TOKEN}
  • ${__V(CSRF_TOKEN)}

Both approaches will work fine. However, the second one uses the __V function, which allows the evaluation of nested variables.  So feel free to replace the recorded value with one of the JMeter Variable references shown in the following image.

Replacing a hard-coded recorded “csrfmiddlewaretoken” request parameter value

Pass a JMeter Variable as a Request Parameter

Now we are ready to re-run a recorded script with the correlation enabled. Again, the View Results Tree listener is the best way to see the requests/responses details.

View Results Tree showing the details of a re-recorded load test with enabled correlation.

Proof of Login Success

Let us take a closer look at this ‘Proof of Login Success’ picture:

Proof of Login success
  1. The second request, which failed during the first recorded scenario, now passes.
  2. The “Logout” and “Profile” links also confirm that the user is authenticated.
  3. The penultimate request reports an error (shown by the red font and exclamation mark) as it hasn’t been correlated and the CSRF token doesn’t match the server’s expectations. 

If you are sure that your request is correct and the token is good, but you are still unable to login, double-check other request parameters, such as the: 

  • Origin header.
  • Referer header.
  • Host header.
  • Any cookies other than “csrf” ones.

The reason to check these parameters is because it is possible that the recorded headers will work in a test environment but fail in staging or production due to an origin mismatch.  

Back to top

Bottom Line

This blog post showed how you can deal with CSRF protection in your Apache JMeter test script. The same approach can also be taken for any other scenario which assumes correlation. 

Just a few final things: Remember that the CSRF token won’t necessarily have “csrf” in its name. Also, a developer can implement his own approach and the application under test might expect something different ( i.e. a specific HTTP Request Header rather than a parameter). Being attentive to anything that changes from request to request and keeping your eyes open is the key to a robust and reliable JMeter test. 

Learn how you can scale your JMeter testing with BlazeMeter. Try BlazeMeter for free today. 


Start Testing Now

Back to top