Dmitri Tikhanski is a Contributing Writer to the BlazeMeter blog.

Learn JMeter in 5 Hours

Start Learning
Slack

Test Your Website Performance NOW!

arrow Please enter a URL with http(s)

How to Load Test CSRF-Protected Web Sites

Does this picture look familiar?

 

If not, get ready to start seeing it a lot! 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. So, in this post, I’m going to show you how to bypass CSRF protection in your JMeter Test.


 

What is CSRF?

 

Let’s take a step back to see what CSRF is and why we need to be protected from it in the first place.

 

Cross-Site Request Forgery is an attack which forces the user to execute unwanted actions on a site he’s authenticated on. 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. The attacker makes these requests for a number of reasons. Here are a few:

 

  1. To log the user out

  2. To change the password

  3. To get access to a restricted resource

  4. To escalate 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 him or her.

 

That’s why different content management systems and frameworks like Drupal, .NET Framework, and Django have built-in protection from CSRF requests. This means that developers don’t usually need to worry about implementing CSRF protection themselves.

 

CSRF and 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 the:

 

  1. Cookie
  2. Header
  3. Request Parameter

 

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’ll 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.

 

Extracting the CSRF Token with JMeter’s PostProcessors

 

Let’s inspect two recorded requests to a Django website to see how they are different. If you’re not comfortable recording JMeter tests, it’s 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

 

Personally I would go for option three as it means that I don’t need to worry about proxies, SSL certificates, excluding common unwanted assets etc.  

 

So let’s record two login attempts and see what’s different:

 

Attempt One:

 

 

 

Attempt Two:

 

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.

 

So let’s add the View Results Tree listener to our recorded test plan and see the response details of the request before login.

 

Run the Recorded Scenario

 

 

  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

 

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' />

 

You can find out more about extracting “interesting” stuff from responses in Blazemeter’s Knowledge Base

 

Now, based on the response nature, you can use the:

 

  1. Regular Expression Extractor

  2. XPath Extractor

  3. CSS/JQuery Extractor

  4. JSON Path Extractor (available via JMeter Plugins). Our article on the XPath Extractor covers the JSON Path as well.

 

I’ve listed below examples of the configurations of each of these PostProcessors.

 

Please note: 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’s meaningful. For example: if you set ‘TOKEN’ as the Reference Name, you’ll be able to access this variable as ${TOKEN} later on in the script.

 

  1. Regular Expression Extractor:
  2. 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”
  3. Reference Name: REGEX_TOKEN
  4. Regular Expression:

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

    • Template: $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.

 

In JMeter Performance and Tuning Tips, we 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.). Check out this famous StackOverflow thread for more on this. If you’re totally sure that the entity you’re 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.


 

Inspect Response Headers

 

 

Take a look at this section:

 

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

 

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

 

To extract it from the Set-Cookie header, add a Regular Expression Extractor PostProcessor 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 the Response Cookie via the Regular Expression Extractor

 

 

Now it’s a good time to replace a hard-coded recorded “csrfmiddlewaretoken” request parameter value with the one we extracted from “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 above.

 

Pass a JMeter Variable as a Request Parameter

 

 

Now we’re 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.

 

Proof of Login Success

 

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

 

  1. The second request, which failed during the run 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’re sure that your request is correct and the token is good, but you aren’t still able to login, double-check other request parameters. These include the:

 

  1. Origin header

  2. Referer header

  3. Host header

  4. Any cookies other than “csrf” ones

 

This is because it’s possible that the recorded headers will work in a test environment but fail in staging or production due to an origin mismatch.  

 

This is 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 it’s name. For example: the Liferay application server uses “p_auth”. Also, a developer could 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.

 

Feel free to ask if you’re still having problems with CSRF or any other correlation. We’ll do our best to help you and may even add a new article on our Knowledge Base or Blog.

 
     
arrow Please enter a URL with http(s)

You might also find these useful:

Interested in writing for our Blog?Send us a pitch!