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