相信前端開發工程師對CSRF(Cross-site request forgery)跨站請求僞造這個概念都很是熟悉,有的時候也簡寫成XSRF,是一種對網站的惡意利用。前端
儘管聽起來像跨站腳本(XSS),但它與XSS很是不一樣,XSS利用站點內的信任用戶,而CSRF則經過假裝成受信任用戶的請求來利用受信任的網站。git
CSRF攻擊的防護方式有多種,最簡單最易實現的一種思路就是在客戶端向服務器發起的請求中放入攻擊者沒法僞造的信息,而且該信息沒有存儲於 cookie 之中。技術上來講,當客戶端向服務器發起請求執行一些敏感操做以前(好比用HTTP post實現的轉帳,扣款等功能),服務器端隨機產生一個token,返回給客戶端。客戶端接下來的操做,必須在HTTP請求中以參數的形式把這個服務器端頒發的token帶上。同時服務器端在實現給客戶端分配token的同時,也要加入一個token校驗機制。若是請求中沒有 token 或者 token 內容不正確,則認爲多是 CSRF 攻擊而拒絕該請求。這個token咱們通常稱爲CSRF token。github
講了這麼多,是爲了引入本文想要討論的話題。假設我想用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裏如何維護並配置這種具備依賴關係的一組請求。json
固然若是您不喜歡用jMeter,想本身寫代碼實現,也是能夠的。能夠參考我放在github上的Java代碼實現。服務器
用jMeter的好處是不須要編程,經過簡單的配置就能實現這個性能測試需求,通常沒有開發背景的測試人員也能獨立完成。cookie
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.app
從下圖能看出,由於拿CSRF token的HTTP GET在邏輯上必須先於實際須要測試性能的HTTP POST請求,這實際上構成了一個Transaction-事務,因此我使用jMeter裏提供的Transaction Controller來管理。
(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.
(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頭部字段的方式返回給客戶端。
下一個問題就是,服務器返回給客戶端合法的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中。
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。
(3) Create another HTTP request with type POST.
這時萬事俱備,咱們能夠開始構造真正要進行性能測試的HTTP post,即Service Ticket的建立請求了。
請求的報文正文: 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值:
我但願最後經過併發測試生成的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.
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:
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:
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隨機數生成器的用法也正確。
但願本文對你們的工做有所幫助。
要獲取更多Jerry的原創文章,請關注公衆號"汪子熙":