Wednesday, October 17, 2018

JMetesr—How to Load Test CSRF-Protected Web Sites

As shown in below Figure (click to enlarge), an X-CSRF-Token header is used in an HTTP request.  In this article, we will discuss how to load test CSRF-Protected web sites using JMeter.  To begin with, what is CSRF?

Figure 1.  X-CSRF-Token header shown in View Results Tree

Cross-Site Request Forgery (CSRF)


Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user's browser. Cross-Site Request Forgery (CSRF) attacks occur through a malicious website that sends the requests to the targeted application website if the user is already authenticated through a different website.

These attacks happen if the user logs in to the actual website and leaves the session open, and accesses the malicious website links and forms that try to form the dynamic URLs to the targeted application where the user has already logged in.

The best way to prevent CSRF attacks is to attach CSRF tokens to each request from the application users and bind them to the user session. This way, the applications can restrict the access to the user's secure information by confirming the request is coming from known user sessions.

View Results Tree


After script recording, the best way to debug correlation issues is to use View Results Tree component (a Listener).

The View Results Tree shows a tree of all sample responses, allowing you to view the response for any sample. In addition to showing the response, you can see the time it took to get this response, and some response codes.

Note that View Results Tree MUST NOT BE USED during load test as it consumes a lot of resources (memory and CPU). Use it only for either functional testing or during Test Plan debugging and Validation.

From Figure 1, we have found an X-CSRF-Token header used in Step 32.  This means that we need to search for CSRF token backwards in the Response Data of earlier Steps.

Correlation


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:[2]
  • Cookie
  • Header
  • 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.  In this article, we will demonstrate the use of a JMeter PostProcessor Regular Expression Extractor to extract the CSRF token.

Figure 2.  Specification of  a PostProcessor Regular Expression Extractor 

Regular Expression Extractor


At Step 15 (see Figure 2), you need to add a new PostProcessor Regular Expression Extractor to extract the CSRF token.

Sample Response Data:

[
  {
   <snipped>
    "bimodelerURL": "/bimodeler",
    "csrftoken": "h3CYF2EDNYlfBPKM01grVQQMfUKE0lAvhwfRzHtxU1Mdigx6",
    "vaAdminPermission": true,
   <snipped>
   }
]

Regular Expression:

"csrftoken":"([^\"]+)


Then, you can use a text editor to open the jmx file and do a global replacement of
h3CYF2EDNYlfBPKM01grVQQMfUKE0lAvhwfRzHtxU1Mdigx6
with
${csrftoken}
For example, at Step 32, you should see something updated like below:

Figure 3.  Variable Substitution in HTTP Header


References

  1. Oracle JET for Developers
  2. How to Load Test CSRF-Protected Web Sites


Monday, August 20, 2018

JMeter—Using "Save Responses to a file" for Error Logging

Using JMeter to do load test, you often see a small percentage of responses failed.  How do you investigate those errors?

In [1], it suggests two approaches:

In this article, we will cover using Save Responses to a file listener with the option of saving responses only in error.  Note that don't forget disabling it in your final run (i.e., after the debugging process).

Save Responses to a file


The primary use for this is in creating functional tests, but it can also be useful where the response is too large to be displayed in the View Results Tree Listener.  Listed below are parameters that you can configure for this test element.




Example


This test element can be placed anywhere in the test plan. For each sample in its scope, it will create a file of the response Data.  In this example, Save Responses to a file listener is placed in MyProfile test element (i.e., a Transaction Controller), there are two samples (i.e., 111 and 112) in its scope.



The file name is created from the specified prefix, plus a number (unless this is disabled). The file extension is created from the document type, if known. If not known, the file extension is set to 'unknown'. If numbering is disabled, and adding a suffix is disabled, then the file prefix is taken as the entire file name. This allows a fixed file name to be generated if required. The generated file name is stored in the sample response, and can be saved in the test log output file if required.

The current sample is saved first, followed by any sub-samples (child samples). If a variable name is provided, then the names of the files are saved in the order that the sub-samples appear. 

In this example, we have specified "MyServices_1832_MyProfile" as the Filename prefix.  By default, it's placed in the JMeter's bin directory.  For example,

-rw-r--r-- 1 root root        0 Aug 19 16:46 MyServices_1832_myProfile25.unknown
-rw-r--r-- 1 root root     2769 Aug 19 16:46 MyServices_1832_myProfile26.unknown

The contents of MyServices_1832_myProfile26.unknown show the following exception:

java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:158)
at org.apache.http.impl.io.SocketInputBuffer.fillBuffer(SocketInputBuffer.java:82)
at org.apache.http.impl.io.AbstractSessionInputBuffer.readLine(AbstractSessionInputBuffer.java:271)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:138)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
<snipped>
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.client.DefaultRequestDirector.createTunnelToTarget(DefaultRequestDirector.java:876)
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:794)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:614)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)
at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:654)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:413)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:74)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1189)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1178)
at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:491)
at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:425)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:254)
at java.lang.Thread.run(Thread.java:745)