天天看點

如何防止别人抓包重播攻擊

現在做過幾個web項目,都用到了api,有伺服器發往伺服器的api,也有微信上從前端發往後端的api。然後,我在使用 Pycharm 斷點時發現,如果在斷點暫停時一直發送請求,那麼很多請求都能執行成功。然後在不斷點的正常情況下,我又使用 Charles 重複發送請求,并發設定為10,一共發100個,也有10幾個請求成功了。

前端向後端發送請求

我知道csrf_token可以防止跨站攻擊,但如果現在是一個惡意使用者怎麼辦?我有一個每日簽到API,調用之後,在資料庫中添加對應記錄,并增加積分。我先在的校驗方法是,如果資料庫中存在一個今天簽到的記錄,就傳回錯誤。現在如果使用者進行請求重放,在資料庫寫入記錄之前發了N條記錄,那麼增加積分的操作就會執行N次了。

現在的思路是,看看微信的做法,它的參數中含有 timestamp 和 nonce,并進行了簽名加密。以及 如何防止别人抓包重播攻擊 的一些思路。

一、微信的做法(微信文檔位址)

  1. 驗證消息的确來自微信伺服器

    通過 timestamp, nonce 以及雙方約定好的 token 構成一個簽名,能夠驗證資訊是否來自微信。

    在這裡并不适用,因為api就是由使用者調用的。

二、  如何防止别人抓包重播攻擊

  希望一個包就實作, 不是反複發包。

  一個包實作并不僅僅指第一個包實作,可以是前n個失敗,然後第n+1個成功了,然後就拒絕之後的包了。如果需要達成這樣,我們要使能夠實作的請求唯一,建構一個類似id這樣,不能重複的東西。

三、 加鎖

類似雙十一搶購,當一個使用者下單之後,就鎖定5分鐘等待其付款,期間拒絕其他使用者的下單請求。我們能否做到,這個請求期間,針對一個 openid 的請求隻進行一次,其他就在後面排隊。當一個成功後,後面類似的請求就看做是失敗的。

有一個同僚做過類似的加鎖,明天問問他(沒問,現在在寫新需求以及調優,還沒做這個)。

問了一個同僚,建議使用 redis 的 sadd 指令,如果訂單号存在于 set 中,就傳回失敗。還有 redis 的分布式鎖,他不建議使用,如果沒有弄清楚分布式鎖的原理。

現在回到我們最初的問題:

如何保證使用者每天隻能簽到一次?

擷取簽到請求後,通過唯一值去 redis 中查找,如果存在,則說明簽到過了,傳回 "今日已簽到"

不存在,則說明今天還沒有簽到,進行簽到流程。

将上面的步驟拆開:

1. 根據使用者的簽到請求建構唯一值

2. 去 redis 中查詢唯一值

3.1 已簽到,拒絕

3.2 未簽到,設定 redis,進行簽到流程

問:為什麼要儲存在 redis 而不是 mysql 中?

  • 因為儲存到 mysql 中太慢了,

  在儲存到硬碟(mysql)的過程中,這個值是不存在的,若這時候使用者再次發送簽到請求,那麼還是可以簽到的。這就違反了"使用者每天隻能簽到一次"

問:如何将資料儲存到 mysql 中?

儲存到 redis 後,使用異步任務将這個資料同步到 mysql。

問:如何确儲存到 redis 就足夠快?

不確定。我本地測試的時候沒發現能夠多次簽到。

儲存到 redis 的速度大于兩次請求的速度就能夠確定。

問:鎖是什麼?這個與鎖有什麼異同?

問:如何查找相關資料?

直接搜尋後,找不到很多資料。那麼我們就把問題誇大或縮小。

誇大:

搜尋`秒殺`

秒殺系統架構分析與實戰

問:能否寫一個函數\裝飾器\上下文管理器實作這個功能?

問:有沒有相關的第三方庫?

伺服器向伺服器發送api

一般的做法是使用AES加密待發送的内容,再使用RSA加密AES秘鑰,然後将以上兩個一起發送出去。令我感到疑惑的是:為什麼需要使用RSA加密AES秘鑰并傳輸,一開始兩邊約定好一個AES秘鑰不就可以了嗎?

是以我現在的做法就是,兩邊約定好AES KEY,然後使用AES加解密内容,并對解密後的内容進行校驗。

在一個抽獎功能中又遇到了庫存的問題

需求:存在 N 種不同的抽獎獎品,抽獎的機率與各獎品的實時數量成正比。希望把所有的獎品發放完畢,又不希望超賣。獎品庫存總量變為 0 後,固定傳回一種獎品。

比如有3中商品,數量為 A:10, B: 20 C: 70。 則開始時抽中 A 的機率為 (10)/(10+20+70) = 10%,

抽中 A 後, 數量變為 A: 9, B: 20 C: 70,則 A 的機率變為 9 / (9 + 20 + 70)

應用場景:現場抽獎。

綜合我的需求與實作難度,決定使用 django transaction + select_for_update 

發現的問題:

select_for_update  隻鎖了行,能讀不能寫,會造成超賣;

解決

使用MySQL表鎖

LOCK TABLE business_bankcard READ;
LOCK TABLE business_bankcard WRITE;

# 其他代碼

UNLOCK TABLES;      

上面的寫法有問題

LOCK TABLES table_name WRITE;

# 其他代碼

UNLOCK TABLES
      

因為 WRITE 的含義就是不可讀、不可寫

Option Description
READ Read lock, no writes allowed
READ LOCAL Read lock, but allow concurrent inserts
WRITE Exclusive write lock. No other connections can read or write to this table
LOW_PRIORITY WRITE Exclusive write lock, but allow new read locks on the table until we get the write lock.
WRITE CONCURRENT Exclusive write lock, but allow READ LOCAL locks to the table.

出處:LOCK TABLES and UNLOCK TABLES

鎖表的注意事項

注意解鎖

注意異常之後的解鎖

比如捕捉異常後,解鎖,再抛出異常

更好的方法

同僚建議使用分布式鎖

redis 單機鎖

參考: 

如何解決秒殺的性能問題和超賣的讨論 

 select for update 帶來的性能問題

測試

如何測試。設定庫存數量後,使用  timesleep 模拟并發的現象

參考:

(未看,待整合)Web大規模高并發請求和搶購的解決方案

(從上面這篇文章中可以了解到,要吸收強者的經驗,秒殺與搶購還有比12306和淘寶更多的嗎?看看他們是怎麼實作的吧!)

日請求從百萬到八億的技術曆程

(規模性的東西還是大公司牛叉)

程式員對比在大公司和創業公司的工作和報酬

如果你關心你做某件事後産生的實際效果,在大公司絕對是有更大的實際效果,歸因于大公司的規模。如果我是在一家創業公司做我目前的工作,獲得的收益大約是每月 1 萬美元。我沒什麼好蔑視,但這都支付不起我的工資。但是同樣的事情在大公司創造的收益會是1萬美元的1000倍以上。在大公司有更大的實際效果因為它的規模很大。這裡的推論是小公司很小以至于他們很容易對自身造成影響,盡管這個影響值本身很小。我感覺不到我做的事情對大公司會産生促進還是阻礙的作用。但是當我在小公司時,看起來我們所做的事情可以影響整個公司的命運。