開放API網關實踐(二) —— 重放攻擊及防禦

如何設計實現一個輕量的開放API網關之重放攻擊及防禦

文章地址: https://blog.piaoruiqing.com/blog/2019/08/11/開放api網關實踐之重放攻擊及防禦/

前言

上一篇文章(開放API網關實踐(一)中的接口設計提到timestampnonce兩個參數的作用是用來放重放. 先拋出兩個問題:

  • 什麼是重放攻擊
  • 如何防禦重放攻擊

什麼是重放攻擊(Replay Attacks)

什麼是重放, 先舉個例子:

打開瀏覽器的調試工具並訪問一個網站, 在網絡工具中找到一個請求並右鍵選擇Replay. 如圖:

Chrome請求重放

上述的重放操作是接口調試中比較常用的手段, 這種操作可以讓我們跳過認證信息的生成過程, 直接重複發起多次有效的請求.

重放攻擊是一種黑客常用的攻擊手段, 又稱重播攻擊回放攻擊, 是指攻擊者發送目的主機已接收過的數據, 以達到欺騙系統的目的, 主要用於身份認證過程, 破壞認證的正確性.

舉個易懂的例子:

  • 服務端提供了打款接口, 用戶A向服務端請求發起一次打款5元的操作(附帶了簽名並進行了加密), 服務端接收到了數據並正確打款給用戶B.
  • 但這個請求被黑客攔截到(可能就是用戶B乾的 ( ̄▽ ̄)"), 黑客將請求原封不動地向服務器發送, 服務器多次錯誤地打款給用戶B. (當然, 這些都是是建立在服務端的付款沒做冪等等防範措施、安全級別較低的前提下)
  • 儘管A發起的請求有簽名和加密, 但B無需破解這個數據, 只是將同樣的數據重複發給服務器就能達到欺騙的目的.

重放攻擊

模擬重放攻擊

實驗器材

序號 名稱 數量 備註
1 服務器x2 1 Mac: 10.33.30.101 - 真實服務器
Windows: 10.33.30.100 - 僞造服務器
2 域名 1 replay-test.piaoruiqing.com (10.33.30.101)
3 DNS服務器 1 用來模擬DNS劫持

實驗步驟

  1. 啓動服務器, 請求接口並收到響應數據.
  2. 劫持DNS(在路由器中修改DNS服務器地址模擬劫持), 並攔截請求數據.
  3. 向服務器重複發送攔截到的數據(重放攻擊).

過程記錄

準備工作

DNS配置, 將域名replay-test.piaoruiqing.com指向內網中服務器的IP. 並啓動服務器.

DNS

正常請求

使用postman發起一個正常的請求, 其中籤名已在Pre-request-script中生成.

預處理請求

通過DNS劫持來攔截數據

修改內網的dnsmasq配置, 將域名replay-test.piaoruiqing.com指向僞造的服務器10.33.30.100.

DNS劫持

圖片描述

此時向replay-test.piaoruiqing.com發起的請求便會被髮送到僞造的服務器上(10.33.30.100), 手動將請求的數據保存下來. 由於請求帶有簽名, 且攻擊者並沒有拿到私鑰, 故無法篡改請求, 但可以進行重放攻擊. 如圖, 僞造服務器已成功接收到請求數據:

攔截數據

[版權聲明]
本文發佈於樸瑞卿的博客, 允許非商業用途轉載, 但轉載必須保留原作者樸瑞卿 及鏈接:blog.piaoruiqing.com. 如有授權方面的協商或合作, 請聯繫郵箱: [email protected].

重放請求

使用上一步保存下來的數據, 直接向真實服務器發送請求(帶有簽名數據). 如圖:

重放

事實上, 簽名、加密等手段並不能防禦重放攻擊, 因爲攻擊者攔截到的數據已是正確的請求數據, 即使無法破解其內容, 也可以重放向服務器發送原數據以達到欺騙的目的.

如何防禦重放攻擊

百度百科
  1. 加隨機數: 該方法優點是認證雙方不需要時間同步,雙方記住使用過的隨機數, 如發現報文中有以前使用過的隨機數, 就認爲是重放攻擊. 缺點是需要額外保存使用過的隨機數, 若記錄的時間段較長, 則保存和查詢的開銷較大.
  2. 加時間戳: 該方法優點是不用額外保存其他信息. 缺點是認證雙方需要準確的時間同步, 同步越好, 受攻擊的可能性就越小. 但當系統很龐大, 跨越的區域較廣時, 要做到精確的時間同步並不是很容易.
  3. 加流水號: 就是雙方在報文中添加一個逐步遞增的整數, 只要接收到一個不連續的流水號報文(太大或太小), 就認定有重放威脅. 該方法優點是不需要時間同步, 保存的信息量比隨機數方式小. 缺點是一旦攻擊者對報文解密成功, 就可以獲得流水號, 從而每次將流水號遞增欺騙認證端.

在實際使用中, 常將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. 如有授權方面的協商或合作, 請聯繫郵箱: [email protected].

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章