التحقق من التوقيع
التوقيع HMAC-SHA256 على «timestamp ثم نقطة ثم جسم الطلب الخام» بسر الاشتراك. أثناء تدوير السر يحمل الرأس توقيعين مفصولين بفاصلة؛ اقبل أيهما. ارفض أي timestamp أقدم من 5 دقائق لمنع إعادة الإرسال.
C#
static bool Verify(string secret, string timestamp, string body, string header)
{
if (!long.TryParse(timestamp, out var ts) || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - ts) > 300) return false;
var mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes($"{timestamp}.{body}"));
var expected = Encoding.ASCII.GetBytes("sha256=" + Convert.ToHexString(mac).ToLowerInvariant());
return header.Split(',').Any(s => CryptographicOperations.FixedTimeEquals(expected, Encoding.ASCII.GetBytes(s.Trim())));
}
Node.js
const crypto = require("crypto");
function verify(secret, timestamp, rawBody, header) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
return header.split(",").some(s => {
const a = Buffer.from(s.trim()), b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
});
}
Python
import hmac, hashlib, time
def verify(secret: str, timestamp: str, raw_body: bytes, header: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
mac = hmac.new(secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest("sha256=" + mac, s.strip()) for s in header.split(","))
PHP
function verify(string $secret, string $timestamp, string $rawBody, string $header): bool {
if (abs(time() - (int)$timestamp) > 300) return false;
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
foreach (explode(',', $header) as $s) { if (hash_equals($expected, trim($s))) return true; }
return false;
}
مهم: احسب التوقيع على الجسم الخام كما وصل، قبل أي تحويل JSON.