背景
最近,公司由于業務繁忙人手不夠,將部分業務外包給第三方公司做。在開發的過程中,遇到了外部調用內部敏感接口場景。為了保證接口的安全,基于微信公眾號消息驗證機制,實現了一套安全驗證流程。
微信公眾號消息驗證流程
當開發者向微信提交信息(含 token
配置)后,微信服務器會發送攜帶參數的GET請求到開發者的服務器,攜帶的驗證信息如下:
參數 | 描述 |
---|---|
signature | 加密簽名 |
timestamp | 時間戳 |
nonce | 隨機數 |
echostr | 隨機字符串 |
開發者將通過檢驗 signature
對請求進行驗證。驗證流程如下:
1)將token、timestamp、nonce三個參數進行字典序排序
2)將三個參數字符串拼接成一個字符串進行sha1加密
3)開發者獲得加密后的字符串可與signature對比,若相同,則驗證成功。
總結一下微信驗證的原理,通過比對開發者生成的簽名與微信發送的簽名,來驗證請求是否合法。
微信與開發者共同維護一個 token
(作為秘鑰,不發生傳遞),使用相同的簽名機制,得到相同的簽名。
驗證機制設計
簽名流程
前提:接口提供方與接入方,協商維護一個token??梢钥醋鲭p方維護的密鑰。
- 請求方攜帶參數請求接口,參數包括
signature
、timestamp
、nonce
、業務參數。
例如,傳送的參數如下:
signature: 8bbf7cde111af0c0655f578c5a84185e8f533880
timestamp: 1535253509
nonce: TyPoTPVYmp
user_id: 1
- 將參數和
token
以字母升序(A-Z)排序,除去signature
。
特別注意:
- 參數名 以字母升序排序
-
signature
不參與排序
const token = 'youthcity';
const { signature, timestamp, nonce, user_id } = ctx.request.query;
const unordered_params = {
timestamp,
nonce,
user_id,
token,
};
const ordered_params = {};
Object.keys(unordered_params).sort().forEach((key) => {
ordered_params[key] = unordered_params[key];
});
- 再以
'key=value'+ '&' + 'key=value'
連接所有參數得到字符串。
let sign_str = Object.keys(ordered_params)
.map((key) => `${key}=${ordered_params[key]}`)
.join('&');
- 將
sign_str
進行 sha1 加密
const expected_signature = crypto.createHash('sha1').update(sign_str).digest('hex');
服務端攔截規則設計
對于開放接口,主要面臨3個安全問題:
- 請求身份是否可信任
- 請求的參數是否被篡改
- 請求是否唯一(防止重放攻擊)
將針對這三個問題,進行攔截設計。
1)驗證請求參數是否包含 signature
、timestamp
、nonce
、業務參數。若參數缺少或類型不合法,則返回錯誤碼。
2)判斷服務端接受請求時間與請求參數中的timestamp
的時間差是否在合理的范圍內(建議為正負 5min
)。若時間差超過合理值,則請求過期,視為無效請求。(即使請求URL中的 timestamp
被篡改,signature
會對其合法性進行校驗)
3)判斷請求是否已被使用。根據 nonce
,判斷緩存中是否存在 KEY
,若存在,則請求已被使用,返回錯誤碼。若不存在,則以 nonce
為KEY,寫入緩存,并設置過期時間為 10min
。
主要目的是為了防止重放攻擊。避免請求參數被泄露,被其他方盜用攻擊。
4)根據請求參數生成簽名。對比簽名是否相同。若相同,則進入業務邏輯,否則,進行攔截。
小結
通過 時間戳(timestamp
)和隨機數(nonce
)我們可以防御大多數的請求重放攻擊。隨機數(nonce
)可以防御短期的重放攻擊,若隨機數過期,則黑客可以繞過隨機數的防御。這時,可以利用時間戳(timestamp
)的長期防御,攔截請求。
優化與改進
- 對于安全等級較高的接口,我們還可以利用IP白名單機制,進行防御。
總結
在簽名驗證設計中,我們將時間戳和隨機數作為驗證的一部分。主要目的是防止請求鏈接盜用。關于時間戳的有效范圍,需要根據實際業務場景和接入方服務情況進行調整。時間范圍太小,可能會導致接入方請求失敗。時間范圍太長,請求唯一性的保證就越弱。
此外,安全驗證只能驗證接入方是否合法。不能保證 token
泄密或者受信任方濫用接口的情況。因此,需要在業務層面,根據具體情況進行二次防御設計。例如,記錄調用歷史,或限制每日調用頻次限制。