開放API網(wǎng)關(guān)實(shí)踐(二) —— 重放攻擊及防御

如何設(shè)計(jì)實(shí)現(xiàn)一個(gè)輕量的開放API網(wǎng)關(guān)之重放攻擊及防御

文章地址: https://blog.piaoruiqing.com/blog/2019/08/11/開放api網(wǎng)關(guān)實(shí)踐之重放攻擊及防御/

前言

上一篇文章(開放API網(wǎng)關(guān)實(shí)踐(一)中的接口設(shè)計(jì)提到timestampnonce兩個(gè)參數(shù)的作用是用來防重放. 本文就重放攻擊及其防御進(jìn)行探討. 先拋出兩個(gè)問題:

  • 什么是重放攻擊
  • 如何防御重放攻擊

什么是重放攻擊(Replay Attacks)

什么是重放, 先舉個(gè)例子:

打開瀏覽器的調(diào)試工具并訪問一個(gè)網(wǎng)站, 在網(wǎng)絡(luò)工具中找到一個(gè)請(qǐng)求并右鍵選擇Replay. 如圖:

Chrome請(qǐng)求重放

上述的重放操作是接口調(diào)試中比較常用的手段, 這種操作可以讓我們跳過認(rèn)證信息的生成過程, 直接重復(fù)發(fā)起多次有效的請(qǐng)求.

重放攻擊是一種黑客常用的攻擊手段, 又稱重播攻擊、回放攻擊, 是指攻擊者發(fā)送目的主機(jī)已接收過的數(shù)據(jù), 以達(dá)到欺騙系統(tǒng)的目的, 主要用于身份認(rèn)證過程, 破壞認(rèn)證的正確性.

舉個(gè)易懂的例子:

  • 服務(wù)端提供了打款接口, 用戶A向服務(wù)端請(qǐng)求發(fā)起一次打款5元的操作(附帶了簽名并進(jìn)行了加密), 服務(wù)端接收到了數(shù)據(jù)并正確打款給用戶B.
  • 但這個(gè)請(qǐng)求被黑客攔截到(可能就是用戶B干的 ( ̄▽ ̄)"), 黑客將請(qǐng)求原封不動(dòng)地向服務(wù)器發(fā)送, 服務(wù)器多次錯(cuò)誤地打款給用戶B. (當(dāng)然, 這些都是是建立在服務(wù)端的付款沒做冪等等防范措施、安全級(jí)別較低的前提下)
  • 盡管A發(fā)起的請(qǐng)求有簽名和加密, 但B無需破解這個(gè)數(shù)據(jù), 只是將同樣的數(shù)據(jù)重復(fù)發(fā)給服務(wù)器就能達(dá)到欺騙的目的.
重放攻擊

模擬重放攻擊

實(shí)驗(yàn)器材

序號(hào) 名稱 數(shù)量 備注
1 服務(wù)器 2 Mac: 10.33.30.101 - 真實(shí)服務(wù)器<br />Windows: 10.33.30.100 - 偽造服務(wù)器
2 域名 1 replay-test.piaoruiqing.com (10.33.30.101)
3 DNS服務(wù)器 1 用來模擬DNS劫持

實(shí)驗(yàn)步驟

  1. 啟動(dòng)服務(wù)器, 請(qǐng)求接口并收到響應(yīng)數(shù)據(jù).
  2. 劫持DNS(在路由器中修改DNS服務(wù)器地址模擬劫持), 并攔截請(qǐng)求數(shù)據(jù).
  3. 向服務(wù)器重復(fù)發(fā)送攔截到的數(shù)據(jù)(重放攻擊).

過程記錄

準(zhǔn)備工作

DNS配置, 將域名replay-test.piaoruiqing.com指向內(nèi)網(wǎng)中服務(wù)器的IP. 并啟動(dòng)服務(wù)器.

DNS

正常請(qǐng)求

使用postman發(fā)起一個(gè)正常的請(qǐng)求, 其中簽名已在Pre-request-script中生成.

預(yù)處理請(qǐng)求

通過DNS劫持來攔截?cái)?shù)據(jù)

修改內(nèi)網(wǎng)的dnsmasq配置, 將域名replay-test.piaoruiqing.com指向偽造的服務(wù)器10.33.30.100.

DNS劫持
DNS劫持

此時(shí)向replay-test.piaoruiqing.com發(fā)起的請(qǐng)求便會(huì)被發(fā)送到偽造的服務(wù)器上(10.33.30.100), 手動(dòng)將請(qǐng)求的數(shù)據(jù)保存下來. 由于請(qǐng)求帶有簽名, 且攻擊者并沒有拿到私鑰, 故無法篡改請(qǐng)求, 但可以進(jìn)行重放攻擊. 如圖, 偽造服務(wù)器已成功接收到請(qǐng)求數(shù)據(jù):

攔截?cái)?shù)據(jù)

[版權(quán)聲明]
本文發(fā)布于樸瑞卿的博客, 允許非商業(yè)用途轉(zhuǎn)載, 但轉(zhuǎn)載必須保留原作者樸瑞卿 及鏈接:blog.piaoruiqing.com. 如有授權(quán)方面的協(xié)商或合作, 請(qǐng)聯(lián)系郵箱: piaoruiqing@gmail.com.

重放請(qǐng)求

使用上一步保存下來的數(shù)據(jù), 直接向真實(shí)服務(wù)器發(fā)送請(qǐng)求(帶有簽名數(shù)據(jù)). 如圖:

重放

事實(shí)上, 簽名、加密等手段并不能防御重放攻擊, 因?yàn)楣粽邤r截到的數(shù)據(jù)已是正確的請(qǐng)求數(shù)據(jù), 即使無法破解其內(nèi)容, 也可以重放向服務(wù)器發(fā)送原數(shù)據(jù)以達(dá)到欺騙的目的.

如何防御重放攻擊

百度百科

  1. 加隨機(jī)數(shù): 該方法優(yōu)點(diǎn)是認(rèn)證雙方不需要時(shí)間同步,雙方記住使用過的隨機(jī)數(shù), 如發(fā)現(xiàn)報(bào)文中有以前使用過的隨機(jī)數(shù), 就認(rèn)為是重放攻擊. 缺點(diǎn)是需要額外保存使用過的隨機(jī)數(shù), 若記錄的時(shí)間段較長, 則保存和查詢的開銷較大.

  2. 加時(shí)間戳: 該方法優(yōu)點(diǎn)是不用額外保存其他信息. 缺點(diǎn)是認(rèn)證雙方需要準(zhǔn)確的時(shí)間同步, 同步越好, 受攻擊的可能性就越小. 但當(dāng)系統(tǒng)很龐大, 跨越的區(qū)域較廣時(shí), 要做到精確的時(shí)間同步并不是很容易.

  3. 加流水號(hào): 就是雙方在報(bào)文中添加一個(gè)逐步遞增的整數(shù), 只要接收到一個(gè)不連續(xù)的流水號(hào)報(bào)文(太大或太小), 就認(rèn)定有重放威脅. 該方法優(yōu)點(diǎn)是不需要時(shí)間同步, 保存的信息量比隨機(jī)數(shù)方式小. 缺點(diǎn)是一旦攻擊者對(duì)報(bào)文解密成功, 就可以獲得流水號(hào), 從而每次將流水號(hào)遞增欺騙認(rèn)證端.

在實(shí)際使用中, 常將1和2結(jié)合使用, 時(shí)間戳有效期內(nèi)判斷隨機(jī)數(shù)是否已存在, 有效期外則直接丟棄.

重放攻擊防御實(shí)踐

我們采取時(shí)間戳+隨機(jī)數(shù)的方式來實(shí)現(xiàn)一個(gè)簡單的重放攻擊攔截器. 時(shí)間戳和隨機(jī)數(shù)互補(bǔ), 既能在時(shí)間有效范圍內(nèi)通過校驗(yàn)緩存中的隨機(jī)數(shù)是否存在來分辨是否為重放請(qǐng)求, 也能在緩存失效后(緩存有效時(shí)間和時(shí)間范圍一致)通過時(shí)間戳來校驗(yàn)該請(qǐng)求是否為重放. 如圖:

重放攻擊防御

代碼如下:

@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)
            );

  • (一): 請(qǐng)求時(shí)間超出時(shí)間范圍的將被拒絕.
  • (二): 緩存過期時(shí)間等于有效時(shí)間的跨度, 若緩存中已存在該隨機(jī)數(shù), 則拒絕.

結(jié)語

重放攻擊防御的關(guān)鍵點(diǎn):

  • 記錄請(qǐng)求標(biāo)識(shí)并緩存, 接受請(qǐng)求時(shí)校驗(yàn), 拒絕重放, 即將nonce存入緩存, 拒絕相同的nonce
  • 隨機(jī)數(shù)的方式可能造成過多的緩存, 故需要配合時(shí)間戳進(jìn)行過濾, 時(shí)間戳不在有效范圍內(nèi)的一律拒絕.

重放攻擊是一種常用且有效的攻擊手段, 其危害不可忽視, 盡管可以通過業(yè)務(wù)層面來保障數(shù)據(jù)的正確性, 但依舊會(huì)給系統(tǒng)造成不必要開銷, 在網(wǎng)關(guān)層過濾掉重放請(qǐng)求是一個(gè)不錯(cuò)的選擇.

系列文章:

歡迎關(guān)注公眾號(hào): 代碼如詩

[版權(quán)聲明]
本文發(fā)布于樸瑞卿的博客, 允許非商業(yè)用途轉(zhuǎn)載, 但轉(zhuǎn)載必須保留原作者樸瑞卿 及鏈接:blog.piaoruiqing.com. 如有授權(quán)方面的協(xié)商或合作, 請(qǐng)聯(lián)系郵箱: piaoruiqing@gmail.com.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容