安全|API接口安全性設(shè)計(jì)(防篡改和重復(fù)調(diào)用)

API接口的安全性主要是為了保證數(shù)據(jù)不會(huì)被篡改和重復(fù)調(diào)用,實(shí)現(xiàn)方案主要圍繞Token、時(shí)間戳和Sign三個(gè)機(jī)制展開設(shè)計(jì)。

1. Token授權(quán)機(jī)制
用戶使用用戶名密碼登錄后服務(wù)器給客戶端返回一個(gè)Token(必須要保證唯一,可以結(jié)合UUID和本地設(shè)備標(biāo)示),并將Token-UserId以鍵值對(duì)的形式存放在緩存服務(wù)器中(我們是使用Redis),并要設(shè)置失效時(shí)間。服務(wù)端接收到請(qǐng)求后進(jìn)行Token驗(yàn)證,如果Token不存在,說明請(qǐng)求無效。Token是客戶端訪問服務(wù)端的憑證。

# uuid 是手機(jī)設(shè)備的唯一標(biāo)示
String token = UUID.randomUUID().toString() + "_" + uuid;

2. 時(shí)間戳超時(shí)機(jī)制
用戶每次請(qǐng)求都帶上當(dāng)前時(shí)間的時(shí)間戳timestamp,服務(wù)端接收到timestamp后跟當(dāng)前時(shí)間進(jìn)行比對(duì),如果時(shí)間差大于一定時(shí)間(比如30秒),則認(rèn)為該請(qǐng)求失效。時(shí)間戳超時(shí)機(jī)制是防御重復(fù)調(diào)用和爬取數(shù)據(jù)的有效手段。
當(dāng)然這里需要注意的地方是保證客戶端和服務(wù)端的“當(dāng)前時(shí)間”是一致的,我們采取的對(duì)齊方式是客戶端第一次連接服務(wù)端時(shí)請(qǐng)求一個(gè)接口獲取服務(wù)端的當(dāng)前時(shí)間A1,再和客戶端的當(dāng)前時(shí)間B1做一個(gè)差異化計(jì)算(A1-B1=AB),得出差異值A(chǔ)B,客戶端再后面的請(qǐng)求中都是傳B1+AB給到服務(wù)端。

// timeStamp是客戶端從Header傳過來的值
Long timeStamp = RequestHeaderContext.getInstance().getTimeStamp();
boolean checkTime = checkTime(timeStamp, 30 * 1000);
if (!checkTime) {
    return responseErrorAPISecurity(response);
}

// checkTime方法
public static boolean checkTime(Long time, Integer variable){
    Long currentTimeMillis = System.currentTimeMillis();
    Long addTime = currentTimeMillis + variable;
    Long subTime = currentTimeMillis - variable;
    if (addTime > time && time > subTime){
        return true;
    }
    return false;
}

3. API簽名機(jī)制
將“請(qǐng)求的API參數(shù)”+“時(shí)間戳”+“鹽”進(jìn)行MD5算法加密,加密后的數(shù)據(jù)就是本次請(qǐng)求的簽名signature,服務(wù)端接收到請(qǐng)求后以同樣的算法得到簽名,并跟當(dāng)前的簽名進(jìn)行比對(duì),如果不一樣,說明參數(shù)被更改過,直接返回錯(cuò)誤標(biāo)識(shí)。簽名機(jī)制保證了數(shù)據(jù)不會(huì)被篡改。

// 請(qǐng)求的API參數(shù),如果是再body,則MD5;如果是param,則原字符串
StringBuffer urlSign = new StringBuffer();

if ("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod())) {
    String bodyStr = RequestReaderUtil.ReadAsChars(request);
    String bodySign = "";
    if (!StringUtils.isEmpty(bodyStr)){
        bodySign = DigestUtils.md5DigestAsHex((bodyStr).getBytes());
    }
    urlSign = new StringBuffer(bodySign);
} else if ("GET".equals(request.getMethod()) || "DELETE".equals(request.getMethod())) {
    String params = request.getQueryString();
    if (params == null){
        params = "";
    }
    urlSign = new StringBuffer(params);
}
// “請(qǐng)求的API參數(shù)”+“時(shí)間戳”+“鹽”進(jìn)行MD5算法加密
String sign = DigestUtils.md5DigestAsHex(urlSign.append(timeStamp).append(salt).toString().getBytes());

// signature是客戶端從Header傳過來的值
if (signature.equals(sign)) {
    return true;
} else {
    return false;
}

4. 注意事項(xiàng)
1、因?yàn)橛脩舻卿浀腡oken是和設(shè)備唯一標(biāo)示綁定的,所以一個(gè)用戶有可能會(huì)有多個(gè)有效的Token,那么當(dāng)用戶在修改登錄密碼時(shí)需要把所有的Token刪除,我的做法是在Redis保存了一個(gè)value是List(值是該用戶所有的有效的Token)的Key,當(dāng)修改密碼時(shí)會(huì)把該Key下的所有Token干掉。
2、客戶端每次請(qǐng)求,在Header里面有timeStamp的值,簽名中也是用這個(gè)timeStamp組合簽名的,要確保這兩個(gè)值是一致的。因?yàn)槲覀冊趯?shí)際開發(fā)中,發(fā)現(xiàn)客戶端的同事在加密時(shí)通過函數(shù)獲取了當(dāng)前時(shí)間A,在請(qǐng)求時(shí)也通過函數(shù)獲取了當(dāng)前時(shí)間B,有時(shí)候這兩個(gè)當(dāng)前時(shí)間會(huì)差幾毫秒,導(dǎo)致簽名校驗(yàn)失敗。

/**
  * 登錄后由服務(wù)端生成并返回
  */
private String token;

/**
  * 安全校驗(yàn)字段(接口參數(shù)+時(shí)間戳+加鹽:取MD5生成)
  */
private String signature;

 /**
  * 設(shè)備唯一標(biāo)識(shí)
  */
private String udid;

/**
  * 時(shí)間戳,13位,比如:1532942172000
  */
private Long timeStamp;

5. 安全保障總結(jié)
在以上機(jī)制下,
如果有人劫持了請(qǐng)求,并對(duì)請(qǐng)求中的參數(shù)進(jìn)行了修改,簽名就無法通過;
如果有人使用已經(jīng)劫持的URL進(jìn)行DOS攻擊和爬取數(shù)據(jù),那么他也只能最多使用30s;
如果簽名算法都泄露了怎么辦?可能性很小,因?yàn)檫@里的“鹽”值只有我們的CTO知道。

最后編輯于
?著作權(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)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,694評(píng)論 19 139
  • API接口安全性設(shè)計(jì) 接口的安全性主要圍繞Token、Timestamp和Sign三個(gè)機(jī)制展開設(shè)計(jì),保證接口的數(shù)據(jù)...
    靳曉陽s閱讀 1,593評(píng)論 0 2
  • API定義規(guī)范 本規(guī)范設(shè)計(jì)基于如下使用場景: 請(qǐng)求頻率不是非常高:如果產(chǎn)品的使用周期內(nèi)請(qǐng)求頻率非常高,建議使用雙通...
    有涯逐無涯閱讀 2,938評(píng)論 0 6
  • 看起來好像前后端分離是個(gè)浪潮,原來只有APP客戶端會(huì)考慮這些,現(xiàn)在連Web都要考慮前后端分離 。這里面不得不談的就...
    Gundy_閱讀 2,917評(píng)論 0 6
  • 有一天,思諾去給外婆送東西吃。趁媽媽不注意,她提著小籃子,穿著橙色的連衣裙,出門了。 外婆的家不遠(yuǎn)...
    毛顯棋閱讀 689評(píng)論 0 0

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