使用jMeter構造邏輯上有依賴關係的一系列併發請求

i042416發表於2018-12-26

相信前端開發工程師對CSRF(Cross-site request forgery)跨站請求偽造這個概念都非常熟悉,有的時候也簡寫成XSRF,是一種對網站的惡意利用。

儘管聽起來像跨站指令碼(XSS),但它與XSS非常不同,XSS利用站點內的信任使用者,而CSRF則通過偽裝成受信任使用者的請求來利用受信任的網站。

2085791-26175b3a2f416e6f.png

CSRF攻擊的防禦方式有多種,最簡單最易實現的一種思路就是在客戶端向伺服器發起的請求中放入攻擊者無法偽造的資訊,並且該資訊沒有儲存於 cookie 之中。技術上來說,當客戶端向伺服器發起請求執行一些敏感操作之前(比如用HTTP post實現的轉賬,扣款等功能),伺服器端隨機產生一個token,返回給客戶端。客戶端接下來的操作,必須在HTTP請求中以引數的形式把這個伺服器端頒發的token帶上。同時伺服器端在實現給客戶端分配token的同時,也要加入一個token校驗機制。如果請求中沒有 token 或者 token 內容不正確,則認為可能是 CSRF 攻擊而拒絕該請求。這個token我們一般稱為CSRF token。

講了這麼多,是為了引入本文想要討論的話題。假設我想用jMeter測試一個OOdata服務建立Service Ticket的效能。因為建立功能不像讀操作,執行之後會對系統產生持久化影響(Persistence side-effect), 因此伺服器端的實現加入了CSRF token的校驗。這就是說,如果我們直接用jMeter構造併發的HTTP post請求,是沒有辦法完成測試的,這些請求因為沒有包含CSRF token,會被伺服器端直接拒絕掉。

根據前面描述的CSRF攻防原理,CSRF token是伺服器端隨機生成的,客戶端無法用任何技術進行偽造,因為為了測試介面HTTP post操作進行Service Ticket的建立,我們必須構造一個它的前置HTTP GET請求,專門用於得到伺服器返回的CSRF token,然後再構造真正用於效能測試的HTTP POST請求,把第一步GET請求獲得的CSRF token附到POST請求的頭部中去。

本文介紹在jMeter裡如何維護並配置這種具有依賴關係的一組請求。

當然如果您不喜歡用jMeter,想自己寫程式碼實現,也是可以的。可以參考我放在github上的Java程式碼實現。

用jMeter的好處是不需要程式設計,通過簡單的配置就能實現這個效能測試需求,一般沒有開發背景的測試人員也能獨立完成。

First let us have a look how JMeter could archive the same without even one line of programming.

My project in JMeter is displayed with the following hierarchy. I have configured with “Number of 5 threads” in my thread group, so once executed, the response time of these 5 threads are displayed in result table together with average response time.

從下圖能看出,因為拿CSRF token的HTTP GET在邏輯上必須先於實際需要測試效能的HTTP POST請求,這實際上構成了一個Transaction-事務,所以我使用jMeter裡提供的Transaction Controller來管理。

2085791-f9125e8aadbd449a.png

Some key points for this JMeter project creation

(1) Since now one thread should cover both XSRF token fetch via HTTP get and Service request creation via HTTP post, so a transaction controller is necessary to include both request.

2085791-0f300725645e47e9.png

(2) Create the first HTTP request to fetch XSRF token. The setting could be found below: adding a http header field with name as
x-csrf-token and value as “fetch”:

在HTTP GET請求的頭部加上一個名為x-csrf-token的欄位,值賦成fetch。這樣伺服器接到這個請求,就知道這是客戶端發起的CSRF token請求,於是伺服器響應這個請求,把建立好的隨機CSRF token通過HTTP response頭部欄位的方式返回給客戶端。

2085791-4d5c8477f73e0f18.png

下一個問題就是,伺服器返回給客戶端合法的CSRF token後,jMeter如何讀取到這個token,並用於接下來的請求?

幸運的是,jMeter提供了正規表示式提取式,可以讓我們很方便地從HTTP響應結構中提取出token來。

Create a Regular Expression Extractor to parse the XSRF token from response header and stored it to a variable named “jerrycsrftoken”.

下圖構造了一個jMeter正規表示式提取器,工作於HTTP響應的頭部欄位,解析出的token值儲存於變數jerrycsrftoken中。

2085791-355116670f5ac9a0.png

Before you continue, please make sure that the XSRF token is correctly parsed from request header, which could be confirmed by printing it out in a debug sample:

這個請求構造完之後,我們先試著執行一次,確保在變數jerrycsrftoken裡確實看到解析好的CSRF token。

2085791-a936e40c938faa5c.png

(3) Create another HTTP request with type POST.

這時萬事俱備,我們可以開始構造真正要進行效能測試的HTTP post,即Service Ticket的建立請求了。

2085791-6c009cf044eaa401.png

請求的報文正文:
Just paste the following text to the tab “Body Data”:

--batch_1
Content-Type: multipart/mixed; boundary=changeset_1

--changeset_1
Content-Type: application/http
Content-Transfer-Encoding: binary

POST ServiceRequestCollection HTTP/1.1
Content-Length: 5000
Accept: application/json
Content-Type: application/json

{
"ServicePriorityCode": "2",
"Name": {"content": "Jerry Testing ticket creation via JMeter ${uuid} "},
"ServiceRequestDescription": [
{
"Text": "Piston Rattling 1 - Generic OData Test Create",
"TypeCode": "10004"
},
{
"Text": "Piston Rattling 2 - Generic OData Test Create",
"TypeCode": "10007"
}
]
}
--changeset_1--

--batch_1--

In the body text I use a user-defined variable ${uuid} which we could create it in last step. And for this post request, use the XSRF token fetched from previous HTTP get request.

前面說過,POST請求的頭部需要加上合法的CSRF token,此處我們使用前面GET請求已經拿到的並且儲存於變數jerrycsrftoken中的token值:

2085791-c50e721ad784ff07.png

我希望最後通過併發測試生成的Service Ticket的描述資訊的字尾是1到100的隨機正整數,因此我使用jMeter裡自帶的一個隨機數發生器:

(4) As the last step, create a user variable by using JMeter built-in function __Random, to create a random number between 1 ~ 100 as a fragment of created Service Request description.

2085791-86ac6d2bc92a9cdd.png

Now execute the Thread group, and the execution detail for these three HTTP request could be reviewed separately in tree view:

試著執行一下,發現這個POST操作確實按照我們期望的那樣,在HTTP頭部欄位里加上了正確合法的CSRF token:

2085791-045ac364d6a4630c.png

For example, the XSRF token is successfully fetched in the first request: rdPy7zNj_uKDYvQLgfQCFA==
And used as one header field in second HTTP Post request as expected:

2085791-5e2274ec904c0ffc.png

And finally in UI we could find the created Service request with random number between 1 ~ 100 as postfix:

在UI上觀測到我構造的5個併發請求建立的Service Ticket,說明CSRF token在伺服器端的校驗成功,同時發現描述資訊都帶上了隨機數,說明我的jMeter隨機數生成器的用法也正確。

2085791-d009d424275e59a7.png

希望本文對大家的工作有所幫助。

要獲取更多Jerry的原創文章,請關注公眾號"汪子熙":

2085791-dcc88e58a67cac8c.png

相關文章