數據安全(反爬蟲)之「防重放」策略
在大前端時代的安全性一文中講了 Web 前端和 Native 客戶端如何從數據安全層面做反爬蟲策略,本文接著之前的背景,將從 API 數據接口的層面講一種技術方案,實現(xiàn)數據安全。
一、 API 接口請求安全性問題
API 接口存在很多常見的安全性問題,常見的有下面幾種情況
- 即使采用 HTTPS,諸如 Charles、Wireshark 之類的專業(yè)抓包工具可以扮演證書頒發(fā)、校驗的角色,因此可以查看到數據
- 拿到請求信息后原封不動的發(fā)起第二個請求,在服務器上生產了部分臟數據(接口是背后的邏輯是對 DB 的數據插入、刪除等)
所以針對上述的問題也有一些解決方案:
- HTTPS 證書的雙向認證解決抓包工具問題
- 假如通過網絡層高手截獲了 HTTPS 加證書認證后的數據,所以需要對請求參數做簽名
- 「防重放策略」解決請求的多次發(fā)起問題
- 請求參數和返回內容做額外 RSA 加密處理,即使截獲,也無法查看到明文。
關于 HTTPS 證書雙向認證和 Web 端反爬蟲技術方案均在大前端時代的安全性一文中有具體講解。接下來引出本文主角:防重放
二、 請求參數防篡改
在之前的文章也講過,HTTPS 依舊可以被抓包,造成安全問題。抓包工具下數據依舊是裸奔的,可以查看Charles 從入門到精通文中講的如何獲取 HTTPS 數據。
假如通過網絡層高手截獲了 HTTPS 加證書認證后的數據,所以需要對請求參數做簽名。步驟如下
- 客戶端使用約定好的密鑰對請求參數進行加密,得到簽名 signature。并將簽名加入到請求參數中,發(fā)送給服務端
- 服務端接收到客戶端請求,使用約定好的密鑰對請求參數(不包括 signature)進行再次簽名,得到值 autograph
- 服務器對比 signature 和 autograph,相等則認為是一次合法請求,否則則認為參數被篡改,判定為一次非法請求
因為中間人不知道簽名密鑰,所以即使攔截到請求,修改了某項參數,但是無法得到正確的簽名 signature,這樣構造的一個請求,會被服務器判定為一次非法請求。
三、 防重放策略
在工程師文化中,我們要做一個事情,就首先要對這個事情下個定義。我們才能知道做什么、怎么做。
理論上,一個 API 接口請求被收到,服務會做校驗,但是當一個合法請求被中間人攔截后,中間人原封不動得重復發(fā)送該請求一次或多次,這種重復利用合法請求進行得攻擊被成為重放。
重放會造成服務器問題,所以我們需要針對重放做防重放。本質上就是如何區(qū)別去一次正常、合法的請求。
3.1 基于 timestamp 的方案
理論上,客戶端發(fā)起一次請求,到服務端接收到這個請求的時間,業(yè)界判定為不超過60秒。利用這個特征,客戶端每次請求都加上 timestamp1,客戶端將 timestamp1 和其他請求參數一起簽名得到 signature,之后發(fā)送請求到服務器。
- 服務器拿到當前時間戳 timestamp2,timestap2 - timestamp1 > 60s,則認為非法
- 服務端接收到客戶端請求,使用約定好的密鑰對請求參數(不包括 signature、timestamp1)進行再次簽名,得到值 autograph。比對 signature 和 autograph,若不相等則認為是一次非法請求
假如中間人攔截到請求,修改了 timestamp 或者其他的任何參數,但是不知道密鑰,所以服務器依舊判定為非法請求。
中間人從抓包、篡改參數、發(fā)起請求的過程一般來說大于60秒,所以服務器依舊會判定為非法請求。
基于 timestamp 的設計缺陷也很明顯,種種原因下,60秒內的請求,會鉆規(guī)則漏洞,服務器判定為一次合法請求。
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秒內的漏網之魚
步驟:
- 客戶端將當前 timestamp1、隨機字符串和其他請求參數,按照密鑰,生成簽名 signature
- 服務端收到請求,利用服務端密鑰,將除 timestamp1、隨機字符串之外的請求參數,加密生成簽名 autograph
- 服務端對比 signature 和 autograph,不相等則認為非法請求
- 拿到服務端時間戳, timestamp2 - timestamp1 < 60,則判定為一次合法請求,然后保存 nonce
- 服務端只保存60秒內的 nonce,定時將集合內過期的 nonce 刪除
該集合不應該直接操作文件或者數據庫,否則服務端 IO 太多,造成性能瓶頸??梢允?mmap 或者其他內存到文件的讀寫機制。根據場景可以選擇樂觀鎖、悲觀鎖。
其中有一個 timestamp 的問題,服務器會將請求參數中的 timestamp 判斷差值,其中一個致命的缺點是服務器的時間和客戶端的時間是存在時間差的,當然你也可以通過校驗時間戳解決此問題。時間同步請繼續(xù)看下面部分。
四、 計算機網絡時間同步技術原理
客戶端和服務端的時間同步在很多場景下非常重要,舉幾個例子,這些場景都是經常發(fā)生的。
- 一個商品秒殺系統(tǒng)。用戶打開頁面,瀏覽各個類目的商品,商品列表界面右側和詳情頁都有倒計時秒殺功能。用戶在詳情頁加購、下單、結算。發(fā)現(xiàn)彈出提示“商品庫存不足,請購買同類其他品牌商品”
- 一個答題系統(tǒng),題目是該公司核心競爭力。所以有心的程序員為接口設計了「防重放」功能。但是前端小哥不給力,接口帶過去的 timestamp 與服務器不在一個時區(qū),差好幾秒。別有用心的競品公司的爬蟲工程師發(fā)現(xiàn)了該漏洞,爬取了題目數據。
所以該現(xiàn)象在計算機領域有非常普遍,有解決方案。
如果精度要求不高的情況下:先請求服務器上的時間 ServerTime,然后記錄下來,同時記錄當前的時間 LocalTime1;需要獲取當前的時間時,用最新的當前時間 (LocalTime2 - LocalTime1 + ServerTime)
拿 iOS 端舉例:
- App 啟動后通過接口獲取服務器時間 ServerTime,保存本地。并同時記錄當前時間 LocalTime1
- 需要使用服務器時間時,先拿到當前時間 LocalTime2 - LocalTime1 + ServerTime
- 若獲取服務器時間接口失敗,則從緩存中拿到之前同步的結果(初始的時間在 App 打包階段內置了)
- 使用 NSSystemClockDidChangeNotification 監(jiān)測系統(tǒng)時間發(fā)生改變,若變化則重新獲取接口,進行時同步
如果需要精度更高,比如 100納秒的情況,則需要使用 NTP(Network Time Protocol)網絡時間協(xié)議、PTP (Precision Time Protocol)精確時間同步協(xié)議了。
原文鏈接:https://segmentfault.com/a/1190000021922705