如何設計實現一個輕量的開放API閘道器之重放攻擊及防禦
前言
上一篇文章《開放API閘道器實踐(一)》中的介面設計提到timestamp
和nonce
兩個引數的作用是用來防重放. 本文就重放攻擊及其防禦進行探討. 先丟擲兩個問題:
- 什麼是重放攻擊
- 如何防禦重放攻擊
什麼是重放攻擊(Replay Attacks)
什麼是重放
, 先舉個例子:
開啟瀏覽器的除錯工具並訪問一個網站, 在網路工具中找到一個請求並右鍵選擇Replay
. 如圖:
上述的重放
操作是介面除錯中比較常用的手段, 這種操作可以讓我們跳過認證資訊的生成過程, 直接重複發起多次有效的請求.
而重放攻擊
是一種黑客常用的攻擊手段, 又稱重播攻擊
、回放攻擊
, 是指攻擊者傳送目的主機已接收過的資料
, 以達到欺騙系統的目的, 主要用於身份認證過程, 破壞認證的正確性.
舉個易懂的例子:
- 服務端提供了打款介面, 使用者A向服務端請求發起一次打款5元的操作(附帶了簽名並進行了加密), 服務端接收到了資料並正確打款給使用者B.
- 但這個請求被黑客攔截到(可能就是使用者B乾的 ( ̄▽ ̄)"), 黑客將請求原封不動地向伺服器傳送, 伺服器多次錯誤地打款給使用者B. (當然, 這些都是是建立在服務端的付款沒做冪等等防範措施、安全級別較低的前提下)
- 儘管A發起的請求有簽名和加密, 但B無需破解這個資料, 只是將
同樣的資料
重複發給伺服器就能達到欺騙的目的.
模擬重放攻擊
實驗器材
序號 | 名稱 | 數量 | 備註 |
---|---|---|---|
1 | 伺服器 | 2 | 10.33.30.101 - 真實伺服器 10.33.30.100 - 偽造伺服器 |
2 | 域名 | 1 | replay-test.piaoruiqing.com (10.33.30.101) |
3 | DNS 伺服器 |
1 | 用來模擬DNS 劫持 |
實驗步驟
- 啟動伺服器, 請求介面並收到響應資料.
- 劫持DNS(在路由器中修改DNS伺服器地址模擬劫持), 並攔截請求資料.
- 向伺服器重複傳送攔截到的資料(重放攻擊).
過程記錄
準備工作
DNS配置, 將域名replay-test.piaoruiqing.com
指向內網中伺服器的IP. 並啟動伺服器.
正常請求
使用postman
發起一個正常的請求, 其中籤名已在Pre-request-script
中生成.
通過DNS劫持來攔截資料
修改內網的dnsmasq
配置, 將域名replay-test.piaoruiqing.com
指向偽造的伺服器10.33.30.100
.
此時向replay-test.piaoruiqing.com
發起的請求便會被髮送到偽造的伺服器上(10.33.30.100), 手動將請求的資料儲存下來. 由於請求帶有簽名, 且攻擊者並沒有拿到私鑰, 故無法篡改請求, 但可以進行重放攻擊. 如圖, 偽造伺服器已成功接收到請求資料:
本文釋出於樸瑞卿的部落格, 允許非商業用途轉載, 但轉載必須保留原作者樸瑞卿 及連結:blog.piaoruiqing.com. 如有授權方面的協商或合作, 請聯絡郵箱: piaoruiqing@gmail.com.
重放請求
使用上一步儲存下來的資料, 直接向真實伺服器傳送請求(帶有簽名資料). 如圖:
事實上, 簽名、加密等手段並不能防禦重放攻擊, 因為攻擊者攔截到的資料已是正確的請求資料, 即使無法破解其內容, 也可以重放向伺服器傳送原資料以達到欺騙的目的.
如何防禦重放攻擊
-
加隨機數
: 該方法優點是認證雙方不需要時間同步,雙方記住使用過的隨機數, 如發現報文中有以前使用過的隨機數, 就認為是重放攻擊. 缺點是需要額外儲存使用過的隨機數, 若記錄的時間段較長, 則儲存和查詢的開銷較大. -
加時間戳
: 該方法優點是不用額外儲存其他資訊. 缺點是認證雙方需要準確的時間同步, 同步越好, 受攻擊的可能性就越小. 但當系統很龐大, 跨越的區域較廣時, 要做到精確的時間同步並不是很容易. -
加流水號
: 就是雙方在報文中新增一個逐步遞增的整數, 只要接收到一個不連續的流水號報文(太大或太小), 就認定有重放威脅. 該方法優點是不需要時間同步, 儲存的資訊量比隨機數方式小. 缺點是一旦攻擊者對報文解密成功, 就可以獲得流水號, 從而每次將流水號遞增欺騙認證端.
在實際使用中, 常將1和2結合使用, 時間戳有效期內判斷隨機數是否已存在, 有效期外則直接丟棄.
重放攻擊防禦實踐
我們採取時間戳
+隨機數
的方式來實現一個簡單的重放攻擊攔截器. 時間戳和隨機數互補, 既能在時間有效範圍內通過校驗快取中的隨機數是否存在來分辨是否為重放請求, 也能在快取失效後(快取有效時間和時間範圍一致)通過時間戳來校驗該請求是否為重放. 如圖:
程式碼如下:
@Resource
private ReactiveStringRedisTemplate reactiveStringRedisTemplate;
private ReactiveValueOperations<String, String> reactiveValueOperations;
@PostConstruct
public void postConstruct() {
reactiveValueOperations = reactiveStringRedisTemplate.opsForValue();
}
@Override
protected Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) {
// 此處的`ATTRIBUTE_OPEN_API_REQUEST_BODY`是前面過濾器存入的
OpenApiRequest<String> body
= exchange.getRequiredAttribute(ATTRIBUTE_OPEN_API_REQUEST_BODY);
if (!ObjectUtils.allNotNull(body, body.getTimestamp(), body.getNonce())) {
return fail(exchange);
}
Long gmt = System.currentTimeMillis();
// (一)
if (gmt + effectiveTimeRange < body.getTimestamp() ||
gmt - effectiveTimeRange > body.getTimestamp()) {
return fail(exchange);
}
// (二)
return reactiveValueOperations.setIfAbsent(MessageFormat.format(
KEY_REPLAY_NONCE, body.getAppId(), body.getNonce()),
String.valueOf(System.currentTimeMillis()),
Duration.ofMillis(effectiveTimeRange * 2L))
.log(LOGGER, Level.FINE, true)
.flatMap(approved -> approved ?
chain.filter(exchange) : fail(FORBIDDEN, exchange)
);
複製程式碼
(一)
: 請求時間超出時間範圍的將被拒絕.(二)
: 快取過期時間等於有效時間的跨度, 若快取中已存在該隨機數, 則拒絕.
結語
重放攻擊防禦的關鍵點:
- 記錄請求標識並快取, 接受請求時校驗, 拒絕重放, 即將
nonce
存入快取, 拒絕相同的nonce
- 隨機數的方式可能造成過多的快取, 故需要配合時間戳進行過濾, 時間戳不在有效範圍內的一律拒絕.
重放攻擊是一種常用且有效的攻擊手段, 其危害不可忽視, 儘管可以通過業務層面來保障資料的正確性, 但依舊會給系統造成不必要開銷, 在閘道器層過濾掉重放請求是一個不錯的選擇.
如果這篇文章對您有幫助,請點個贊吧 ( ̄▽ ̄)"
系列文章:
歡迎關注公眾號:
本文釋出於樸瑞卿的部落格, 允許非商業用途轉載, 但轉載必須保留原作者樸瑞卿 及連結:blog.piaoruiqing.com. 如有授權方面的協商或合作, 請聯絡郵箱: piaoruiqing@gmail.com.