I'm trying to emulate this Svix webhook: https://www.standardwebhooks.com/simulate/svix. I need to generate a signature by combining the svix-id
header with the svix-timestamp
header and the raw body of the request, and it shall equal the svix-signature
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())))?
.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()));
}
result
shall equal svix_signature_header
but I'm getting the error of "Invalid signature". This means result
does not equal svix_signature_header
.
As mentioned on the Rust discord, you are not parsing the svix-signature
header to compare the actual signatures. There is additional metadata in the header.
Also please note that you should use constant-time comparison functions for verifying the hash to prevent timing attacks.