如何設計實現一個輕量的開放API網關之重放攻擊及防護java
文章地址: blog.piaoruiqing.com/2019/08/11/…react
上一篇文章《開放API網關實踐(一)》中的接口設計提到timestamp
和nonce
兩個參數的做用是用來防重放. 本文就重放攻擊及其防護進行探討. 先拋出兩個問題:api
什麼是重放
, 先舉個例子:瀏覽器
打開瀏覽器的調試工具並訪問一個網站, 在網絡工具中找到一個請求並右鍵選擇Replay
. 如圖:緩存
上述的重放
操做是接口調試中比較經常使用的手段, 這種操做可讓咱們跳過認證信息的生成過程, 直接重複發起屢次有效的請求.安全
而重放攻擊
是一種黑客經常使用的攻擊手段, 又稱重播攻擊
、回放攻擊
, 是指攻擊者發送目的主機已接收過的數據
, 以達到欺騙系統的目的, 主要用於身份認證過程, 破壞認證的正確性.服務器
舉個易懂的例子:markdown
一樣的數據
重複發給服務器就能達到欺騙的目的.序號 | 名稱 | 數量 | 備註 |
---|---|---|---|
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配置, 將域名replay-test.piaoruiqing.com
指向內網中服務器的IP. 並啓動服務器.網絡
使用postman
發起一個正常的請求, 其中籤名已在Pre-request-script
中生成.app
修改內網的dnsmasq
配置, 將域名replay-test.piaoruiqing.com
指向僞造的服務器10.33.30.100
.
此時向replay-test.piaoruiqing.com
發起的請求便會被髮送到僞造的服務器上(10.33.30.100), 手動將請求的數據保存下來. 因爲請求帶有簽名, 且攻擊者並無拿到私鑰, 故沒法篡改請求, 但能夠進行重放攻擊. 如圖, 僞造服務器已成功接收到請求數據:
使用上一步保存下來的數據, 直接向真實服務器發送請求(帶有簽名數據). 如圖:
事實上, 簽名、加密等手段並不能防護重放攻擊, 由於攻擊者攔截到的數據已經是正確的請求數據, 即便沒法破解其內容, 也能夠重放向服務器發送原數據以達到欺騙的目的.
加隨機數
: 該方法優勢是認證雙方不須要時間同步,雙方記住使用過的隨機數, 如發現報文中有之前使用過的隨機數, 就認爲是重放攻擊. 缺點是須要額外保存使用過的隨機數, 若記錄的時間段較長, 則保存和查詢的開銷較大.
加時間戳
: 該方法優勢是不用額外保存其餘信息. 缺點是認證雙方須要準確的時間同步, 同步越好, 受攻擊的可能性就越小. 但當系統很龐大, 跨越的區域較廣時, 要作到精確的時間同步並非很容易.
加流水號
: 就是雙方在報文中添加一個逐步遞增的整數, 只要接收到一個不連續的流水號報文(太大或過小), 就認定有重放威脅. 該方法優勢是不須要時間同步, 保存的信息量比隨機數方式小. 缺點是一旦攻擊者對報文解密成功, 就能夠得到流水號, 從而每次將流水號遞增欺騙認證端.
在實際使用中, 常將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
重放攻擊是一種經常使用且有效的攻擊手段, 其危害不可忽視, 儘管能夠經過業務層面來保障數據的正確性, 但依舊會給系統形成沒必要要開銷, 在網關層過濾掉重放請求是一個不錯的選擇.
若是這篇文章對您有幫助,請點個贊吧 ( ̄▽ ̄)"
歡迎關注公衆號: