risingwavelabs/risingwave · error
Signature verification failed
Error message
Signature verification failed
What it means
The webhook payload's computed signature did not match the signature supplied by the client (after building the comparison expression and evaluating it). This returns HTTP 401 UNAUTHORIZED and aborts request processing. It protects webhook endpoints from unauthenticated or tampered payloads.
Solutions
- Verify the client signs the exact raw request body bytes that are sent.
- Confirm the webhook secret matches the one configured in RisingWave.
- Check the signature algorithm and header name match RisingWave's expectations.
- Ensure no intermediary (proxy/gateway) rewrites the body or headers.
- Log both computed and provided signatures to diagnose mismatch.
Example fix
// before
const signature = crypto.createHmac('sha256', secret).update(JSON.stringify(body)).digest('hex');
// after
const signature = crypto.createHmac('sha256', secret).update(rawBodyBuffer).digest('hex'); // sign raw bytes Defensive patterns
Strategy: try-catch
Validate before calling
// client-side sanity check before sending
if (!signature || !rawBody) throw new Error('signature and raw body are both required'); Try / catch
// JS fetch
try {
const res = await fetch(url, { method: 'POST', body: rawBody, headers: { signature } });
if (res.status === 401) throw new Error('webhook signature rejected: check secret and raw-body signing');
} catch (e) { /* log secret name + body hash for diagnosis */ } Prevention
- Always sign the exact raw request bytes, never re-serialized JSON.
- Store the webhook secret in one shared config source to avoid drift.
- Document and pin the signature algorithm and header name.
- Log both computed and expected signature hashes in a debug mode.
When it happens
Trigger: POSTing to a webhook-protected table via handle_post_request or try_handle_connection with a missing, malformed, or incorrect signature header, or with a body that was modified after signing.
Common situations: Signing the wrong payload (e.g. signing pretty-printed JSON while sending compact), wrong secret configured, proxies altering the body, clock/algorithm mismatches, sending raw vs re-serialized body.
Related errors
- `SECURE_COMPARE()` failed
- adlsgen2.authority_host must not contain userinfo
- adlsgen2.authority_host must use the https scheme, got
- adlsgen2: cannot configure both shared-key auth…
- adlsgen2: service-principal auth requires all three of…
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/080927864321b75e.
Report an issue: GitHub.
Appendix: source
Thrown at src/frontend/src/webhook/utils.rs:127
payload: &[u8],
webhook_source_info: &risingwave_pb::catalog::WebhookSourceInfo,
) -> Result<()> {
let is_valid = if let Some(signature_expr) = webhook_source_info.signature_expr.clone() {
let secret = if let Some(secret_ref) = webhook_source_info.secret_ref {
LocalSecretManager::global()
.fill_secret(secret_ref)
.map_err(|e| err(e, StatusCode::NOT_FOUND))?
} else {
String::new()
};
verify_signature(headers_jsonb, secret.as_str(), payload, signature_expr).await?
} else {
true
};
if !is_valid {
return Err(err(
anyhow!("Signature verification failed"),
StatusCode::UNAUTHORIZED,
));
}
Ok(())
}
pub(crate) async fn verify_signature(
headers_jsonb: JsonbVal,
secret: &str,
payload: &[u8],
signature_expr: ExprNode,
) -> Result<bool> {
let row = OwnedRow::new(vec![
Some(headers_jsonb.into()),
Some(secret.into()),
Some(payload.into()),
]);View on GitHub (pinned to 6469eb736d)