forked from molecule-ai/molecule-core
fix(security): constant-time webhook_secret comparison (#337)
Severity LOW. The /webhooks/:type handler compared the Telegram X-Telegram-Bot-Api-Secret-Token header against the decrypted webhook_secret using Go's `!=` operator, which short-circuits on the first mismatched byte. Under low-latency Docker-network conditions an attacker could time response latency byte-by-byte and converge on the real secret, then inject Telegram-formatted messages into any channel. Fix: switch to crypto/subtle.ConstantTimeCompare, which runs in time proportional to the length of the shorter input regardless of content match. Same posture as the cdp-proxy token compare in host-bridge (which already used timingSafeEqual). Risk profile over the public internet is low (Telegram webhooks have natural jitter that masks the signal), but the defensive pattern matters for consistency across all secret comparisons. Closes #337 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c11d8f3ec3
commit
50819500f0
@ -2,6 +2,7 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
@ -423,10 +424,17 @@ func (h *ChannelHandler) Webhook(c *gin.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify webhook secret_token if the channel has one configured
|
||||
// Verify webhook secret_token if the channel has one configured.
|
||||
// #337: use constant-time comparison. Go's `!=` short-circuits on
|
||||
// the first mismatched byte and leaks timing information; an
|
||||
// attacker on the Docker network could enumerate the secret
|
||||
// byte-by-byte. subtle.ConstantTimeCompare runs in time
|
||||
// proportional to the length of the shorter input and returns
|
||||
// 1 on match / 0 otherwise (never -1). Same posture as the
|
||||
// cdp-proxy token compare in host-bridge.
|
||||
if expectedSecret, _ := row.Config["webhook_secret"].(string); expectedSecret != "" {
|
||||
receivedSecret := c.GetHeader("X-Telegram-Bot-Api-Secret-Token")
|
||||
if receivedSecret != expectedSecret {
|
||||
if subtle.ConstantTimeCompare([]byte(receivedSecret), []byte(expectedSecret)) != 1 {
|
||||
continue // Wrong secret — try other channels (could be different bot)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user