資料安全(反爬蟲)之「防重放」策略

杭城小劉發表於2020-03-05

資料安全(反爬蟲)之「防重放」策略

大前端時代的安全性一文中講了 Web 前端和 Native 客戶端如何從資料安全層面做反爬蟲策略,本文接著之前的背景,將從 API 資料介面的層面講一種技術方案,實現資料安全。

一、 API 介面請求安全性問題

API 介面存在很多常見的安全性問題,常見的有下面幾種情況

  1. 即使採用 HTTPS,諸如 Charles、Wireshark 之類的專業抓包工具可以扮演證書頒發、校驗的角色,因此可以檢視到資料
  2. 拿到請求資訊後原封不動的發起第二個請求,在伺服器上生產了部分髒資料(介面是背後的邏輯是對 DB 的資料插入、刪除等)

所以針對上述的問題也有一些解決方案:

  1. HTTPS 證書的雙向認證解決抓包工具問題
  2. 假如通過網路層高手截獲了 HTTPS 加證書認證後的資料,所以需要對請求引數做簽名
  3. 「防重放策略」解決請求的多次發起問題
  4. 請求引數和返回內容做額外 RSA 加密處理,即使截獲,也無法檢視到明文。

關於 HTTPS 證書雙向認證和 Web 端反爬蟲技術方案均在大前端時代的安全性一文中有具體講解。接下來引出本文主角:防重放

二、 請求引數防篡改

在之前的文章也講過,HTTPS 依舊可以被抓包,造成安全問題。抓包工具下資料依舊是裸奔的,可以檢視Charles 從入門到精通文中講的如何獲取 HTTPS 資料。

假如通過網路層高手截獲了 HTTPS 加證書認證後的資料,所以需要對請求引數做簽名。步驟如下

  • 客戶端使用約定好的金鑰對請求引數進行加密,得到簽名 signature。並將簽名加入到請求引數中,傳送給服務端
  • 服務端接收到客戶端請求,使用約定好的金鑰對請求引數(不包括 signature)進行再次簽名,得到值 autograph
  • 伺服器對比 signature 和 autograph,相等則認為是一次合法請求,否則則認為引數被篡改,判定為一次非法請求

因為中間人不知道簽名金鑰,所以即使攔截到請求,修改了某項引數,但是無法得到正確的簽名 signature,這樣構造的一個請求,會被伺服器判定為一次非法請求。

三、 防重放策略

在工程師文化中,我們要做一個事情,就首先要對這個事情下個定義。我們才能知道做什麼、怎麼做。

理論上,一個 API 介面請求被收到,服務會做校驗,但是當一個合法請求被中間人攔截後,中間人原封不動得重複傳送該請求一次或多次,這種重複利用合法請求進行得攻擊被成為重放

重放會造成伺服器問題,所以我們需要針對重放做防重放。本質上就是如何區別去一次正常、合法的請求。

3.1 基於 timestamp 的方案

理論上,客戶端發起一次請求,到服務端接收到這個請求的時間,業界判定為不超過60秒。利用這個特徵,客戶端每次請求都加上 timestamp1,客戶端將 timestamp1 和其他請求引數一起簽名得到 signature,之後傳送請求到伺服器。

  • 伺服器拿到當前時間戳 timestamp2,timestap2 - timestamp1 > 60s,則認為非法
  • 服務端接收到客戶端請求,使用約定好的金鑰對請求引數(不包括 signature、timestamp1)進行再次簽名,得到值 autograph。比對 signature 和 autograph,若不相等則認為是一次非法請求

假如中間人攔截到請求,修改了 timestamp 或者其他的任何引數,但是不知道金鑰,所以伺服器依舊判定為非法請求。 中間人從抓包、篡改引數、發起請求的過程一般來說大於60秒,所以伺服器依舊會判定為非法請求。

基於 timestamp 的設計缺陷也很明顯,種種原因下,60秒內的請求,會鑽規則漏洞,伺服器判定為一次合法請求。

3.2 基於 nonce 的方案

既然時間戳會有漏洞,那麼新方案是基於隨機字串 nonce。也就是說每次請求都加入一個隨機字串,然後將其他引數一起利用金鑰加密得到簽名 signature。服務端收到請求後

  • 先判斷 nonce 引數是否能存在於某個集合中,如果存在則認為是非法請求;如果不存在,則將 nonce 新增到當前的集合中
  • 服務端將客戶端請求引數(除 nonce)結合金鑰加密得到 autograph,將 signature 和 autograph 比對,不相等則認為非法請求

但是該方案也有缺點,因為當次的請求都需要和集合中去搜尋匹配,所以該集合不能太大,不然匹配演算法特別耗時,介面效能降低。所以不得不定期刪除部分 nonce 值。但是這樣的情況下,被刪除的 nonce 被利用為重放攻擊,伺服器判定為合法請求。

假設伺服器只儲存24小時內請求的 nonce,該儲存仍舊是一筆不小的開銷。

3.3 基於 timestamp + nonce 的方案

根據 timestamp 和 nonce 各自的特點:timestamp 無法解決60秒內的重放請求;nonce 儲存和查詢消耗較大。所以結合2者的特點,便有了 「timestamp + nonce 的防重放方案」。

  • 利用 timestamp 解決超過60秒被認為非法請求的問題
  • 利用 nonce 解決 timestamp 60秒內的漏網之魚

步驟:

  1. 客戶端將當前 timestamp1、隨機字串和其他請求引數,按照金鑰,生成簽名 signature
  2. 服務端收到請求,利用服務端金鑰,將除 timestamp1、隨機字串之外的請求引數,加密生成簽名 autograph
  3. 服務端對比 signature 和 autograph,不相等則認為非法請求
  4. 拿到服務端時間戳, timestamp2 - timestamp1 < 60,則判定為一次合法請求,然後儲存 nonce
  5. 服務端只儲存60秒內的 nonce,定時將集合內過期的 nonce 刪除

該集合不應該直接操作檔案或者資料庫,否則服務端 IO 太多,造成效能瓶頸。可以是 mmap 或者其他記憶體到檔案的讀寫機制。根據場景可以選擇樂觀鎖、悲觀鎖。

其中有一個 timestamp 的問題,伺服器會將請求引數中的 timestamp 判斷差值,其中一個致命的缺點是伺服器的時間和客戶端的時間是存在時間差的,當然你也可以通過校驗時間戳解決此問題。時間同步請繼續看下面部分。

四、 計算機網路時間同步技術原理

客戶端和服務端的時間同步在很多場景下非常重要,舉幾個例子,這些場景都是經常發生的。

  • 一個商品秒殺系統。使用者開啟頁面,瀏覽各個類目的商品,商品列表介面右側和詳情頁都有倒數計時秒殺功能。使用者在詳情頁加購、下單、結算。發現彈出提示“商品庫存不足,請購買同類其他品牌商品”
  • 一個答題系統,題目是該公司核心競爭力。所以有心的程式設計師為介面設計了「防重放」功能。但是前端小哥不給力,介面帶過去的 timestamp 與伺服器不在一個時區,差好幾秒。別有用心的競品公司的爬蟲工程師發現了該漏洞,爬取了題目資料。

所以該現象在計算機領域有非常普遍,有解決方案。

  1. 如果精度要求不高的情況下:先請求伺服器上的時間 ServerTime,然後記錄下來,同時記錄當前的時間 LocalTime1;需要獲取當前的時間時,用最新的當前時間 (LocalTime2 - LocalTime1 + ServerTime)

    拿 iOS 端舉例:

    • App 啟動後通過介面獲取伺服器時間 ServerTime,儲存本地。並同時記錄當前時間 LocalTime1
    • 需要使用伺服器時間時,先拿到當前時間 LocalTime2 - LocalTime1 + ServerTime
    • 若獲取伺服器時間介面失敗,則從快取中拿到之前同步的結果(初始的時間在 App 打包階段內建了)
    • 使用 NSSystemClockDidChangeNotification 監測系統時間發生改變,若變化則重新獲取介面,進行時同步
  2. 如果需要精度更高,比如 100納秒的情況,則需要使用 NTP(Network Time Protocol)網路時間協議、PTP (Precision Time Protocol)精確時間同步協議了。

NTP、PTP 不在本文的範疇,感興趣的可以檢視這篇文章

相關文章