交易訂單的重複提交雖然一般不會直接影響現金流和商品流,但依然會給網站運營方帶來損害,如消耗系統資源、影響正經常使用戶訂單生成、製造惡意用戶發起糾紛的機會等。假若訂單對象是虛擬商品,也有可能形成實際損失。訂單重複提交的檢查工做本應該由網站自身實現,而 iFlow 業務安全加固平臺則能夠爲未實現這項功能的網站提供防禦。php
以某開源購物網站爲例,攻擊者可以輕鬆實現訂單的重複提交。咱們看看如何在不修改網站源代碼的前提下,使用 iFlow 經過透明加入一次性令牌來阻止訂單的重複提交攻擊。程序員
原始網站系統沒有檢查訂單的重複提交,攻擊者能夠簡單地重複提交訂單。編程
已登陸用戶在選擇購買一件商品後,進入到確認訂單頁面:json
用戶點擊提交訂單按鈕後,網站回覆訂單已生成:後端
能夠在個人訂單列表中看到剛纔的訂單:瀏覽器
訂單生成的交互過程反映在 HTTP 協議層面以下:安全
攻擊者使用 Burpsuite 工具做爲瀏覽器和 Web 服務器之間的代理。服務器
攻擊者象正經常使用戶同樣選擇商品和確認提交後,可以在 Burpsuite 中的 HTTP history
中找到這個提交訂單信息的請求。攻擊者右鍵點擊 Send to Repeater
後進入 Repeater
標籤頁。dom
攻擊者經過屢次點擊 Send
按鈕來重複發出請求報文從而重複產生訂單,並能夠在個人訂單中看到多個重複生成的訂單,以下圖所示:編程語言
HTTP 協議層面交互以下:
咱們在 Web 服務器前部署 iFlow 業務安全加固平臺,它有能力攔截、計算和修改雙向 HTTP 報文並具有存儲能力,成爲 Web 應用的虛擬補丁。在本例中,iFlow 在加載訂單支付代碼時生成並加入一次性隨機令牌,在提交訂單時檢查這個令牌的存在。
用戶在訪問確認訂單頁面時,瀏覽器自動加載處理訂單支付的 JS 代碼 (payment_orders.js)。iFlow 截獲這段代碼的響應返回,生成一個隨機令牌保存在本地存儲中,並修改 JS 代碼將隨機令牌加入到 AJAX 發送列表中。用戶在點擊提交訂單按鈕時,JS 代碼發出 AJAX 請求將隨機令牌隨同訂單信息一塊兒發出,iFlow 截獲請求,檢查參數中的令牌是否與保存的令牌一致,並清除本地存儲中保存的令牌。對於一個正經常使用戶來講,它們必定是相同的,因而 iFlow 去掉令牌參數,將僅包含訂單信息的請求發往 Web 服務器處理。
正經常使用戶的 HTTP 協議交互過程以下:
如前所示,攻擊者記錄下正常操做時的提交訂單的請求報文,而後用工具重放這段報文。因爲在第一次正常提交後,iFlow 已經清除了本地存儲中保存的令牌,所以後續的重複提交被 iFlow 拒絕。
攻擊者的 HTTP 協議交互過程以下:
iFlow 內置的 W2 語言是一種專門用於實現 Web 應用安全加固的類編程語言。它介於配置和通用語言之間,具有編程的基本要素和針對 HTTP 協議的特有擴展,能爲業務系統編寫涉及複雜判斷和動態修改的邏輯。
考慮到安全產品的使用者一般爲非程序員,他們習慣面對配置文件而非一段代碼。所以,W2 語言雖包含語言要素,仍以規則文件方式呈現,並採用能夠體現層次結構和方便詞法校驗的 JSON 格式。
用 W2 語言實現上述虛擬補丁的代碼以下:
[ { "if": "REQUEST_FILENAME == '/js/payment_orders/payment_orders.js'", "then": { "execution": [ "TX.raw_token = md5(random())", "SESSION.order_token@300 = TX.raw_token", "TX.js_part = '\"' .. TX.raw_token .. '\"'", { "directive": "alterResponseBody", "op": "string", "target": "'leavemessage' : leavemessage,", "substitute": "'leavemessage' : leavemessage, 'order_token' : ${TX.js_part}," } ] } }, { "if": [ "REQUEST_METHOD == 'POST'", "REQUEST_FILENAME == '/index.php'", "@ARGS.s == '/order/ordercreate'" ], "then": { "if": "SESSION.order_token", "then": { "if": "@ARGS.order_token != SESSION.order_token", "then": { "verdict": { "action": "drop", "log": "${@ARGS.order_token} is not equal to ${SESSION.order_token}!" } }, "else": [ "SESSIOIN.order_token = null", { "directive": "alterArgPost", "op": "unset", "name": "order_token" } ] }, "else": { "verdict": { "action": "drop", "log": "${SESSION.order_token} is not exist!" } } } } ]
示例代碼中有兩條規則,分別做用以下:
第一條規則
當瀏覽器請求 payment_orders.js
時,iFlow 攔截響應報文。它首先生成一個隨機令牌 raw_token
並將其存放在會話 (SESSION) 存儲變量 order_token
中,而後修改處理用戶提交訂單的 AJAX 操做,將隨機令牌加入到 POST 的發送參數列表中。
第二條規則
當用戶執行提交訂單時,JS 發出一個 AJAX 的 POST 請求,iFlow 攔截此請求。它檢查會話 (SESSION) 存儲變量 order_token
和參數中的 order_token
,若是前者不存在或者二者不相等,即斷定爲非法請求。不然,將存儲變量 order_token
清除,將請求參數 order_token
消除 (以避免影響後端應用),而後發給後端 Web 服務器。
注意:上述會話中的
order_token
標誌是保存在服務器端的 iFlow 存儲中的,在瀏覽器端是看不到數據更沒法進行僞造的。
iFlow 使用兩條規則在不修改服務器端代碼的前提下,透明地實現了隨機令牌的一次性發放和使用,避免了簡單的重複提交。
固然,若是攻擊者徹底模擬用戶正常操做,重複發起包含先後 2 次會話的攻擊行爲,則本文中的規則沒法阻擋這種重複提交。但顯然這種行爲須要更復雜的攻擊技巧而不僅依靠簡單地重放實現,況且,使用 iFlow 還能構建出更復雜的防禦策略。(張戈 | 天存信息)