You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust实现Svix Webhook签名验证失败:无法生成正确HMAC签名

Svix Webhook签名验证失败问题

我在模拟Svix Webhook签名验证时,需要将svix-id请求头、svix-timestamp请求头与原始请求Body组合生成签名,要求与svix-signature请求头一致,但当前实现返回"Invalid signature"错误,签名不匹配。

我的实现代码

let svix_id_header = &req
    .headers()
    .get("svix-id")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-id header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-id header".to_string())))?
    .to_string();

let svix_timestamp_header = &req
    .headers()
    .get("svix-timestamp")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-timestamp header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-id header".to_string())))?
    .to_string();

let svix_signature_header = &req
    .headers()
    .get("svix-signature")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-signature header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-id header".to_string())))?
    .to_string();

let body_bytes = to_bytes(req.into_body(), usize::MAX).await.unwrap();
let body_string = String::from_utf8(body_bytes.to_vec()).unwrap();

let secret_bytes = state.webhook_secret.as_bytes();
let signed_content = format!("{}.{}.{}", svix_id_header, svix_timestamp_header, body_string);

let mut signature = HmacSha256::new_from_slice(secret_bytes).or_else(|_| Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to create HMAC".to_string())))?;
signature.update(signed_content.as_bytes());

let result = signature.finalize().into_bytes();
let result = base64::encode(&result.to_vec());

if result != *svix_signature_header {
    return Err((StatusCode::UNAUTHORIZED, "Invalid signature".to_string()));
}

问题原因及修正方案

1. Body处理错误(核心问题)

Svix签名要求使用原始请求Body的字节流计算,但你将Body转成String再拼接,可能因编码转换导致字节不一致(比如非UTF-8字符、转义处理等)。需直接用原始字节拼接三个部分。

2. 签名头格式未解析

svix-signature头的格式通常为v1=<base64签名>,需提取等号后的签名值再对比,而非直接使用整个头字符串。

3. 错误提示文案错误

处理svix-timestamp和svix-signature的错误提示误写为"Invalid svix-id header",需修正为对应字段的提示。

修正后的代码

let svix_id_header = &req
    .headers()
    .get("svix-id")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-id header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-id header".to_string())))?;

let svix_timestamp_header = &req
    .headers()
    .get("svix-timestamp")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-timestamp header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-timestamp header".to_string())))?;

// 解析svix-signature头,提取v1版本的签名值
let svix_signature_header = req
    .headers()
    .get("svix-signature")
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Missing svix-signature header".to_string()))?
    .to_str()
    .or_else(|_| Err((StatusCode::BAD_REQUEST, "Invalid svix-signature header".to_string())))?;
let expected_signature = svix_signature_header
    .split('=')
    .nth(1)
    .ok_or_else(|| (StatusCode::BAD_REQUEST, "Invalid svix-signature format".to_string()))?;

// 获取原始Body字节,不转成String
let body_bytes = to_bytes(req.into_body(), usize::MAX).await
    .map_err(|_| (StatusCode::BAD_REQUEST, "Failed to read request body".to_string()))?;

let secret_bytes = state.webhook_secret.as_bytes();
let mut signature = HmacSha256::new_from_slice(secret_bytes)
    .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Failed to create HMAC".to_string()))?;

// 按顺序更新HMAC:svix-id + 点 + timestamp + 点 + 原始Body字节
signature.update(svix_id_header.as_bytes());
signature.update(b".");
signature.update(svix_timestamp_header.as_bytes());
signature.update(b".");
signature.update(&body_bytes);

let result = signature.finalize().into_bytes();
let result = base64::encode(&result);

if result != expected_signature {
    return Err((StatusCode::UNAUTHORIZED, "Invalid signature".to_string()));
}

内容的提问来源于stack exchange,提问作者Kadiem Alqazzaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 08:42:23