1. 程式人生 > 實用技巧 >記錄下:安全|API介面安全性設計(防篡改和重複呼叫)

記錄下:安全|API介面安全性設計(防篡改和重複呼叫)

API介面的安全性主要是為了保證資料不會被篡改和重複呼叫,實現方案主要圍繞Token、時間戳和Sign三個機制展開設計。

1. Token授權機制
使用者使用使用者名稱密碼登入後伺服器給客戶端返回一個Token(必須要保證唯一,可以結合UUID和本地裝置標示),並將Token-UserId以鍵值對的形式存放在快取伺服器中(我們是使用Redis),並要設定失效時間。服務端接收到請求後進行Token驗證,如果Token不存在,說明請求無效。Token是客戶端訪問服務端的憑證。

# uuid 是手機裝置的唯一標示
String token = UUID.randomUUID().toString() + "_" + uuid;
2. 時間戳超時機制
使用者每次請求都帶上當前時間的時間戳timestamp,服務端接收到timestamp後跟當前時間進行比對,如果時間差大於一定時間(比如30秒),則認為該請求失效。時間戳超時機制是防禦重複呼叫和爬取資料的有效手段。
當然這裡需要注意的地方是保證客戶端和服務端的“當前時間”是一致的,我們採取的對齊方式是客戶端第一次連線服務端時請求一個介面獲取服務端的當前時間A1,再和客戶端的當前時間B1做一個差異化計算(A1-B1=AB),得出差異值AB,客戶端再後面的請求中都是傳B1+AB給到服務端。
// 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簽名機制
將“請求的API引數”+“時間戳”+“鹽”進行MD5演算法加密,加密後的資料就是本次請求的簽名signature,服務端接收到請求後以同樣的演算法得到簽名,並跟當前的簽名進行比對,如果不一樣,說明引數被更改過,直接返回錯誤標識。簽名機制保證了資料不會被篡改。
// 請求的API引數,如果是再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);
}
// “請求的API引數”+“時間戳”+“鹽”進行MD5演算法加密
String sign = DigestUtils.md5DigestAsHex(urlSign.append(timeStamp).append(salt).toString().getBytes());

// signature是客戶端從Header傳過來的值
if (signature.equals(sign)) {
    return true;
} else {
    return false;
}
4. 注意事項
/**
  * 登入後由服務端生成並返回
  */
private String token;

/**
  * 安全校驗欄位(介面引數+時間戳+加鹽:取MD5生成)
  */
private String signature;

 /**
  * 裝置唯一標識
  */
private String udid;

/**
  * 時間戳,13位,比如:1532942172000
  */
private Long timeStamp;
5. 安全保障總結
在以上機制下,
如果有人劫持了請求,並對請求中的引數進行了修改,簽名就無法通過;
如果有人使用已經劫持的URL進行DOS攻擊和爬取資料,那麼他也只能最多使用30s;
如果簽名演算法都洩露了怎麼辦?可能性很小,因為這裡的“鹽”值只有我們自己知道。