你開放的接口真的就很安全嗎,看看有沒有做到如下幾點(diǎn)
1.請(qǐng)求身份驗(yàn)證
2.請(qǐng)求參數(shù)校驗(yàn)
3.請(qǐng)求是否唯一
4.請(qǐng)求次數(shù)限制
請(qǐng)求身份驗(yàn)證
基于AccessKey:為接口調(diào)用放分配AccessKey和SecretKey(不參與傳輸,只用于本地接口加密,不能泄露)
基于token身份驗(yàn)證:
1.用戶登錄提供認(rèn)證信息(如:賬號(hào)密碼)服務(wù)器驗(yàn)證成功后將用戶信息保存到token內(nèi)并設(shè)置有效期,再返回token給調(diào)用方
2.調(diào)用方保存token,并在有效期內(nèi)重新?lián)Q取token,保證token是有效的
3.服務(wù)器驗(yàn)證token有效性,無效則攔截請(qǐng)求返回錯(cuò)誤信息,反之則從token內(nèi)獲取用戶信息進(jìn)行后續(xù)操作
請(qǐng)求參數(shù)校驗(yàn)
1.校驗(yàn)參數(shù)合理性(如:參數(shù)類型,參數(shù)長(zhǎng)度,參數(shù)值校驗(yàn))
2.防止XSS,SQL注入(解決方案:過濾敏感字符或直接返回錯(cuò)誤信息)
3.校驗(yàn)參數(shù)可靠性是否被篡改(可以將參數(shù)以特定格式排列+秘鑰組成字符串,在進(jìn)行MD5或SHA簽名)
請(qǐng)求是否唯一
前面第3點(diǎn)解決了請(qǐng)求參數(shù)被篡改的隱患,但是還存在著重復(fù)使用請(qǐng)求參數(shù)偽造二次請(qǐng)求的隱患
timestamp+nonce方案
nonce指唯一的隨機(jī)字符串 ,用來標(biāo)識(shí)每個(gè)被簽名的請(qǐng)求。通過為每個(gè)請(qǐng)求提供一個(gè)唯一的標(biāo)識(shí)符,服務(wù)器能夠防止請(qǐng)求被多次使用(記錄所有用過的nonce以阻止它們被二次使用)。
然而,對(duì)服務(wù)器來說永久存儲(chǔ)所有接收到的nonce的代價(jià)是非常大的??梢允褂胻imestamp來優(yōu)化nonce的存儲(chǔ) 。
假設(shè)允許客戶端和服務(wù)端最多能存在15分鐘的時(shí)間差,同時(shí)追蹤記錄在服務(wù)端的nonce集合。當(dāng)有新的請(qǐng)求進(jìn)入時(shí),首先檢查攜帶的timestamp是否在15分鐘內(nèi),如超出時(shí)間范圍,則拒絕,然后查詢攜帶的nonce,如存在已有集合,則拒絕。否則,記錄該nonce,并刪除集合內(nèi)時(shí)間戳大于15分鐘的nonce(可以使用redis的expire,新增nonce的同時(shí)設(shè)置它的超時(shí)失效時(shí)間為15分鐘)。
請(qǐng)求次數(shù)限制
某些資源我們需要限制用戶的請(qǐng)求次數(shù),同時(shí)也為了防止非人為操作可能導(dǎo)致系統(tǒng)的崩潰
實(shí)現(xiàn)思路如下:
假如我們?cè)试S用戶每秒鐘最多10次請(qǐng)求,超過10次則返回“手速太快了,慢點(diǎn)把。。”
這里我們使用redis輔助我們實(shí)現(xiàn):
以用戶IP為key,請(qǐng)求次數(shù)為value,有效時(shí)間為1秒
用戶在每秒的第一次訪問的時(shí)候,此時(shí)我們的redis是沒有key為用戶ip的數(shù)據(jù)的(因?yàn)槭Я?,或者第一次?qǐng)求)所以我們要初始化當(dāng)前請(qǐng)求用戶的ip為keyvalue為0到redis數(shù)據(jù)庫(kù)
當(dāng)用戶在1s內(nèi)再次發(fā)起請(qǐng)求我們就將此ip的請(qǐng)求次數(shù)+1,并判斷請(qǐng)求次數(shù)是否已近>=10
>=10則返回給用戶手速太快了!請(qǐng)稍后重試..否則繼續(xù)執(zhí)行后續(xù)操作
具體實(shí)現(xiàn)代碼如下:
Boolean hasIp = redisUtil.hasKey(key);
if (!hasIp) {
//初始化ip=0
redisUtil.setEx(key, "0", 1, TimeUnit.SECONDS);
return true;
}
int reqCount = 0;
String s = redisUtil.get(key);
if (!StringUtils.isEmpty(s)) {
reqCount = Integer.parseInt(s);
}
if (reqCount >= 10) {
//請(qǐng)求次數(shù)大于10限制訪問
throw new ApiRRException("手速太快了!請(qǐng)稍后重試..");
}
//對(duì)請(qǐng)求次數(shù)++
redisUtil.setEx(key, (reqCount + 1) + "", 1, TimeUnit.SECONDS);