github webhook 验签需用 $_server['http_x_hub_signature_256'] 与 secret 做 hmac-sha256 校验,必须取原始 payload(file_get_contents('php://input'))、拆解 sha256=xxx 前缀、hex2bin 后比对;微信验签须严格校验 msg_signature、timestamp、nonce 三参数,按 $token.$timestamp.$nonce.$msg 排序拼接后哈希,且关闭 csrf、确保时钟同步。

GitHub Webhook 签名怎么验?X-Hub-Signature-256 是关键
Yii2 默认不自动解析 GitHub 的签名头,得自己读 $_SERVER['HTTP_X_HUB_SIGNATURE_256'],再用你配在 GitHub 后台的 secret 做 HMAC-SHA256 校验。漏掉大小写或前缀就直接失败。
实操建议:
- GitHub 发来的 body 是原始 JSON 字节流,不能先
json_decode(file_get_contents('php://input'))再验签——那样会破坏原始字节顺序,签名必错 - 必须用
file_get_contents('php://input')拿原始 payload,且不能有任何 trim、decode、UTF8 重编码 - 签名格式是
sha256=xxx,得先explode('=', $sig, 2)拆开,取第二段做 hex2bin,再跟hash_hmac('sha256', $rawBody, $secret, true)对比 - Yii2 的
Yii::$app->request->getRawBody()可以替代file_get_contents('php://input'),但要注意它可能被中间件提前读过一次(比如日志组件),导致二次调用返回空
微信回调验签为什么总失败?msg_signature 和 timestamp 必须严格对齐
微信企业微信/公众号回调要求三个参数:msg_signature、timestamp、nonce,缺一不可。少传一个,或者时间戳差过 5 分钟,验签直接返回无效。
实操建议:
- 别信 $_GET 里的
timestamp—— 微信有时会把参数拼在 URL 里,但 Yii2 的Yii::$app->request->get()默认只读 query string,而 POST body 里也可能带一份,得确认来源 - 验签字符串拼接顺序固定:
$tmpArr = [$token, $timestamp, $nonce, $msg]; sort($tmpArr); $tmpStr = implode('', $tmpArr);,其中$msg是解密前的原始Encrypt字段值(不是解密后的内容) - 微信的
$token是你在后台配置的 Token,不是 AppID 或 AppSecret;别和公众号后台的「消息加解密密钥」encodingAESKey混用 - Yii2 接收时若启用了 CSRF 验证,微信回调会被拦截——必须在对应 action 上加
public function beforeAction($action) { Yii::$app->request->enableCsrfValidation = false; return parent::beforeAction($action); }
Yii2 路由怎么接 Webhook?别用普通控制器动作
Webhook 是无状态、高并发、低延迟的纯数据通道,用标准 SiteController::actionIndex() 这类带 layout、session、auth 的动作,既慢又容易出错。
实操建议:
- 单独建个轻量 controller,比如
WebhookController,在behaviors()里关掉AuthBehavior、VerbFilter(除非你明确要限制 method)、ContentNegotiator - 路由别走默认
site/webhook,用无前缀规则,例如在urlManager中加:['pattern' => 'github-webhook', 'route' => 'webhook/github', 'verb' => 'POST'] - 入口脚本最好绕过完整应用生命周期:直接在
web/index.php里加判断,匹配路径后立即处理并 exit,避免加载全部模块(尤其 DB 组件初始化很耗时) - 别在 action 里写业务逻辑——只做验签 + 解包 + 投递到队列(如
Yii::$app->queue->push(new ProcessGithubEventJob(['payload' => $data]))),否则超时或崩溃会导致 GitHub 重试风暴
验签通过后怎么安全解包?警惕 json_decode 的陷阱
GitHub 和微信都发 JSON,但字段结构、编码、嵌套深度差异很大。直接 json_decode($body, true) 可能返回 null,却不报错,后续代码全崩。
实操建议:
- 必须检查
json_last_error() === JSON_ERROR_NONE,否则立刻返回 400;别依赖is_array()判断 - GitHub 的
repository.full_name是字符串,但某些第三方工具(如 Probot)可能改写 header 导致字段缺失;微信的Encrypt是 base64 编码的密文,必须先base64_decode()再 AES 解密,顺序错一步就解不出 - Yii2 的
Json::decode()默认会把数字转成 float,大整数(如 GitHub 的pull_request.id)可能精度丢失;建议用原生json_decode($body, true, 512, JSON_BIGINT_AS_STRING) - 别把解包后的数组直接 dump 到日志——敏感字段(如微信的
FromUserName、GitHub 的sender.login)可能含用户标识,日志系统没脱敏会违规
最麻烦的其实是时间窗口和重放攻击——GitHub 允许最多 5 分钟偏差,微信要求 timestamp 与服务器时间差 ≤ 5 分钟,但 Yii2 默认用 time(),如果服务器时钟不准,验签就会间歇性失败。这事没法靠代码完全规避,得盯住 NTP 同步状态。











