Poco 使用手册
WebHook

签名校验

使用 HMAC-SHA256 校验 WebHook 原始请求体,并通过时间窗口降低重放风险。

配置签名密钥后,Poco 会在每次投递中发送 X-Poco-Signature。生产接收端应同时校验签名和时间戳,再解析和处理 JSON。

签名算法

  1. 读取 X-Poco-Timestamp,记为 timestamp。
  2. 读取 HTTP 原始请求体字节,记为 body_bytes。
  3. 拼接 timestamp.encode("utf-8") + b"." + body_bytes。
  4. 以 WebHook 密钥执行 HMAC-SHA256。
  5. 将十六进制摘要加上 sha256= 前缀。

公式:

signature = "sha256=" + hex(HMAC_SHA256(secret, timestamp + "." + raw_body))

必须使用网络收到的原始请求体。先解析 JSON 再重新序列化会改变空格、转义或键顺序,导致验签失败。

Python 实现

import hashlib
import hmac


def build_signature(secret: str, timestamp: str, body_bytes: bytes) -> str:
    signing_payload = timestamp.encode("utf-8") + b"." + body_bytes
    digest = hmac.new(
        secret.encode("utf-8"),
        signing_payload,
        hashlib.sha256,
    ).hexdigest()
    return f"sha256={digest}"


def verify_signature(
    secret: str,
    timestamp: str,
    body_bytes: bytes,
    signature_from_header: str,
) -> bool:
    expected = build_signature(secret, timestamp, body_bytes)
    return hmac.compare_digest(expected, signature_from_header or "")

使用 hmac.compare_digest,不要用普通字符串 == 比较签名。

校验时间戳

签名本身不能阻止攻击者重放一份曾经合法的请求。接收端应拒绝与当前时间相差过大的 X-Poco-Timestamp,例如只接受 5 分钟内的请求:

from datetime import datetime, timedelta, timezone


def is_timestamp_valid(timestamp: str, max_skew_minutes: int = 5) -> bool:
    try:
        value = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
    except ValueError:
        return False

    now = datetime.now(timezone.utc)
    delta = abs(now - value.astimezone(timezone.utc))
    return delta <= timedelta(minutes=max_skew_minutes)

服务器时钟必须同步。时间窗口太小可能让网络延迟或时钟偏差造成误拒绝;5 分钟是常见起点,最终按部署环境决定。

推荐校验顺序

  1. 检查 X-Poco-Event、X-Poco-Event-Id、X-Poco-Timestamp 和 X-Poco-Signature 是否存在。
  2. 读取原始请求体字节。
  3. 校验时间戳窗口。
  4. 计算签名并使用恒定时间比较。
  5. 解析 JSON。
  6. 检查请求头与请求体中的事件类型、事件 ID 是否一致。
  7. 使用事件 ID 做幂等判断。
  8. 把事件写入可靠队列并返回 2xx。

密钥管理

  • 使用足够长的随机密钥,不要使用账号、项目名等可猜测文本。
  • Poco 配置响应只返回 has_secret,不会返回原密钥;创建后应由双方安全保存。
  • 不要在日志中记录签名密钥、完整请求头或敏感载荷。
  • 轮换密钥时需要协调发送端和接收端,避免新旧密钥切换窗口内大量验签失败。

未配置密钥时 Poco 不发送 X-Poco-Signature。生产环境不建议关闭签名。

On this page