跳至主要内容

簽名校驗

每個 Webhook 請求都攜帶簽名,你應在接收端驗證簽名以確保請求來自 TWT Chat。

簽名算法

  • 算法:HMAC-SHA256
  • 密鑰:你的 AppSecret
  • 簽名對象:請求體原文(raw body)
  • 簽名結果:Hex 編碼(小寫)

簽名值通過請求頭 X-Chat-Signature 傳遞。

校驗流程

  1. 從請求頭中獲取 X-Chat-Signature 的值
  2. 使用 AppSecret 對請求體原文進行 HMAC-SHA256 計算
  3. 將計算結果(Hex 小寫)與 X-Chat-Signature 比較
  4. 一致則驗證通過,否則拒絕該請求

代碼示例

PHP

<?php
$rawBody = file_get_contents('php://input');
$appSecret = 'YOUR_APP_SECRET';
$signature = hash_hmac('sha256', $rawBody, $appSecret);

$headerSignature = $_SERVER['HTTP_X_CHAT_SIGNATURE'] ?? '';

if (hash_equals($signature, $headerSignature)) {
// 驗證通過
} else {
// 驗證失敗,拒絕請求
http_response_code(403);
exit;
}

Python 3

import hmac
import hashlib

raw_body = request.get_data(as_text=True)
app_secret = 'YOUR_APP_SECRET'

signature = hmac.new(
app_secret.encode(),
raw_body.encode(),
hashlib.sha256
).hexdigest()

header_signature = request.headers.get('X-Chat-Signature', '')

if hmac.compare_digest(signature, header_signature):
# 驗證通過
pass
else:
# 驗證失敗
abort(403)

Node.js

const crypto = require('crypto')

const rawBody = req.body // 需要原始字符串,非解析後的對象
const appSecret = 'YOUR_APP_SECRET'

const signature = crypto
.createHmac('sha256', appSecret)
.update(rawBody)
.digest('hex')

const headerSignature = req.headers['x-chat-signature'] || ''

if (signature === headerSignature) {
// 驗證通過
} else {
// 驗證失敗
res.status(403).end()
}

Go

import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)

func verifySignature(rawBody []byte, appSecret, headerSignature string) bool {
mac := hmac.New(sha256.New, []byte(appSecret))
mac.Write(rawBody)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(headerSignature))
}

Java (JDK 8+)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

String rawBody = // 獲取請求體原文
String appSecret = "YOUR_APP_SECRET";

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256"));
byte[] hash = mac.doFinal(rawBody.getBytes("UTF-8"));

StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
String signature = sb.toString();

// 與請求頭中的 X-Chat-Signature 比較