feat: 支持 HMAC-SHA256 签名校验并优化测试用例

这个提交包含在:
wikiboss
2026-09-12 16:46:58 +08:00
父节点 2f822bcabc
当前提交 3646371fc5
共有 2 个文件被更改,包括 56 次插入 和 4 次删除

查看文件

@@ -20,9 +20,8 @@ class WebhookHandler
public function handle(array $server, array $get, string $rawBody): array
{
// 1. 验证 Token (支持 Header 与 URL 参数)
$token = $server['HTTP_X_GITEA_TOKEN'] ?? $server['HTTP_X_GIT_TOKEN'] ?? $server['HTTP_X_GITLAB_TOKEN'] ?? ($get['token'] ?? '');
if ($token === '' || !hash_equals($this->secret, $token)) {
// 1. 验证权限 (支持 Header 明文 Token、URL Token 以及 HMAC-SHA256 签名)
if (!$this->verifyAuth($server, $get, $rawBody)) {
return ['code' => 403, 'data' => ['status' => 'error', 'message' => 'Invalid token']];
}
@@ -33,7 +32,7 @@ class WebhookHandler
}
// 3. 处理 Ping / Test 事件
$event = strtolower($server['HTTP_X_GITEA_EVENT'] ?? $server['HTTP_X_GIT_EVENT'] ?? '');
$event = strtolower($server['HTTP_X_GITEA_EVENT'] ?? $server['HTTP_X_GIT_EVENT'] ?? $server['HTTP_X_GITHUB_EVENT'] ?? '');
if ($event === 'ping') {
return ['code' => 200, 'data' => ['status' => 'success', 'message' => 'pong']];
}
@@ -69,4 +68,27 @@ class WebhookHandler
]
];
}
private function verifyAuth(array $server, array $get, string $rawBody): bool
{
// A. 明文 Token 校验
$token = $server['HTTP_X_GITEA_TOKEN'] ?? $server['HTTP_X_GIT_TOKEN'] ?? $server['HTTP_X_GITLAB_TOKEN'] ?? ($get['token'] ?? '');
if ($token !== '' && hash_equals($this->secret, $token)) {
return true;
}
// B. HMAC-SHA256 签名校验 (Gitea / GitHub)
$signature = $server['HTTP_X_GITEA_SIGNATURE'] ?? $server['HTTP_X_HUB_SIGNATURE_256'] ?? '';
if ($signature !== '') {
if (strpos($signature, 'sha256=') === 0) {
$signature = substr($signature, 7);
}
$expected = hash_hmac('sha256', $rawBody, $this->secret);
if (hash_equals($expected, $signature)) {
return true;
}
}
return false;
}
}

查看文件

@@ -15,6 +15,8 @@ class WebhookTest
$this->testInvalidTokenRejection();
$this->testUrlTokenAllowed();
$this->testHeaderTokenAllowed();
$this->testHmacSha256SignatureAllowed();
$this->testHubSignature256Allowed();
$this->testNonPostMethodRejection();
$this->testPingEventSuccess();
$this->testIgnoredBranchSkipped();
@@ -75,6 +77,34 @@ class WebhookTest
$this->assert($res['code'] === 200, "合法 Header Token 应返回 HTTP 200");
}
/**
* 测试通过 Gitea HMAC-SHA256 签名校验
*/
public function testHmacSha256SignatureAllowed()
{
$handler = new \app\common\library\WebhookHandler('test_secret_123');
$body = json_encode(['ref' => 'refs/heads/master']);
$sig = hash_hmac('sha256', $body, 'test_secret_123');
$server = ['REQUEST_METHOD' => 'POST', 'HTTP_X_GITEA_SIGNATURE' => $sig];
$res = $handler->handle($server, [], $body);
$this->assert($res['code'] === 200, "合法 HMAC-SHA256 签名应返回 HTTP 200");
}
/**
* 测试通过 GitHub/Gitea sha256= 前缀签名校验
*/
public function testHubSignature256Allowed()
{
$handler = new \app\common\library\WebhookHandler('test_secret_123');
$body = json_encode(['ref' => 'refs/heads/master']);
$sig = 'sha256=' . hash_hmac('sha256', $body, 'test_secret_123');
$server = ['REQUEST_METHOD' => 'POST', 'HTTP_X_HUB_SIGNATURE_256' => $sig];
$res = $handler->handle($server, [], $body);
$this->assert($res['code'] === 200, "合法 X-Hub-Signature-256 签名应返回 HTTP 200");
}
/**
* 测试非 POST 请求被拒绝
*/