防篡改
為什么要防篡改
http 是一種無狀態(tài)的協(xié)議, 服務端并不知道客戶端發(fā)送的請求是否合法, 也并不知道請求中的參數(shù)是否正確
舉個栗子, 現(xiàn)在有個充值的接口, 調用給用戶對應的余額
http://localhost/api/user/recharge?user_id=1001&amount=10
- 給指定id的用戶加上10塊錢的余額
- 如果用戶id被篡改,余額參數(shù)被篡改,也就是說,可以給任何用戶加余額
如何防篡改 - 設計sign
- 客戶端: 每次請求客戶端都帶一個
sign
參數(shù)給服務端, 這個所謂的sign
就是一個字符串 - 服務端: 每次處理請求之前先驗證
sign
是否合法, 如果不合法就不處理 - 生成 sign 和驗證 sign: 怎么生成和驗證, 需要客戶端和服務端約定好
3.1. 客戶端生成 sign: 可使用 公私鑰非對稱加密 的方式, 也可以使用計算字符串 md5 或者 hash 值的方式, 這個被加密的字符串最好不是固定的,取時間戳, 請求參數(shù)等就可以, 每次客戶端把生成sign傳遞給服務端
3.2. 服務端根據(jù)約定好的算法驗證sign是否正確就可以
不一定非得叫 sign 這么個名字, 就是個請求參數(shù)的名稱而已
http://localhos/api/assets/recharge?id=1001&amount=10&sign=asdfasdf6sdfs87f67
防重放
設計了防篡改之后, 接口總算是安全了那么一點點, 但是還不夠...還需要對接口設計防重放設置
為什么要防重放
防重放也叫防復用,簡單來說,就是我獲取到這個請求的信息之后, 我什么也不改, 我就拿著接口的參數(shù)去 重復請求這個充值的接口
,也就是說我的請求是合法的, 因為所有參數(shù)都是跟合法請求一模一樣的,也就是說: 服務端的 sign 驗證一定能通過, 但是此時, 我可以去重復請求這個充值的接口, 也就是我能夠重復的充值(假設這個接口沒有做其他邏輯處理,調用就能充值,我只是假設, 別抬杠
), 調用一次加 10 塊錢余額, 2次就是20...這就不合理了
防重放設計
客戶端在請求中添加兩個參數(shù)
1.1 添加一個隨機不重復的字符串參數(shù) 比如uuid
至于怎么讓他不重復,可以考慮拼接時間戳,md5隨機數(shù)等
1.2 添加一個請求時間的參數(shù) 如request_time
值就是發(fā)送請求時的時間戳
服務端接收到請求之后:
2.1 去緩存里中查找 uuid 這個參數(shù)對應的值是否存在
2.2 如果不存在: 就把這個uuid的值保存到緩存中, 記錄這個請求
2.3 如果已存在: 存在那就證明, 已經(jīng)請求過一次了, 就不處理這個請求了
緩存可以是redis也可以是其他存儲介質,應該給緩存設計過期時間,因為請求多了,就會有大量的 uuid
保存在緩存中
參考
這個防重放的設計是參考了這位前輩的經(jīng)驗總結, 有時間可以看看
https://learnku.com/articles/4196/talk-about-the-anti-replay-mechanism-of-api
http://localhos/api/assets/recharge?id=1001&amount=10&sign=asdfasdffs87f67&request_time=1561095355627&uuid=1561095355627aUjKs