GitHub WebHook 使用教程

什麼是 WebHook

WebHook 直譯是網絡鉤子,可以把 WebHook 看做一種通知方式,只要發生關注的事件,就會發送通知到我們指定的 Web 服務。使用 WebHook 可以讓我們在關注的事件發生時收到通知,而不是不斷輪訓 API 確認事件是否發生,

GitHub 允許我們配置 WebHook ,它允許我們配置某個倉庫發生某個事件時,通知指定的外部服務。比如當指定倉庫有提交代碼時,GitHub 將向我們配置的 API 發送 POST 請求。以此可以實現相應的自動化操作,如持續集成 CI,請求代碼審覈,拉取新代碼編譯打包部署等。

發揮想象力,GitHub 的 WebHook 結合 GitHub Action 可以做很多事情,文末有具體例子。

這篇文章介紹 GitHub Web Hooks 的使用。

WebHook 配置

訪問

訪問 GitHub 倉庫的 Webhooks 設置頁面,打開倉庫的 Settings 配置頁面,點擊 Webhook Tab 頁。

也可以直接訪問鏈接:https://github.com/用戶/倉庫/settings/hooks

配置

點擊 Add webhook 按鈕。

配置 GitHub Web Hook

配置完成後,點擊 Add webhook ,GitHub 會發送一個 POST 請求到配置的 web 服務用於驗證是否正常。Web Hook 的事件會在請求頭 X-GitHub-Event 中進行標識。

如上面說到的 ping ,會在請求頭中進行標識:X-GitHub-Event: ping。’

一些說明:

Ping 測試

點擊 Add webhook 後發送一個請求到配置的 Web 服務,下面是一個真實的 ping 事件請求頭信息。

Request URL: https://www.wdbyte.com/api/github/webhook
Request method: POST
Accept: */*
Content-Type: application/x-www-form-urlencoded
User-Agent: GitHub-Hookshot/eb2eabb
X-GitHub-Delivery: 10957020-e918-11ee-8a3e-e0754488dbc
X-GitHub-Event: ping
X-GitHub-Hook-ID: 468323437
X-GitHub-Hook-Installation-Target-ID: 33701056
X-GitHub-Hook-Installation-Target-Type: repository
X-Hub-Signature: sha1=1877dfa1d840bdd5583af33718cea722d825ed7
X-Hub-Signature-256: sha256=addaf81ed2f5795f06ba096b1c863b00188d3444367b5125d2036e17ca324e3

WebHook 消息驗證

因爲配置的 Web 服務 URL 是一個開放的 URL,任何人都可以訪問,爲了防止有人惡意構造 WebHook 消息請求,我們應該對收到的請求進行驗證,判斷是否爲來自 GitHub Web Hook 的請求。

如何驗證呢?是怎麼樣的一個流程呢?這時就要用到上面配置的 Secret 安全密鑰了。

具體步驟如下:

  1. 1. GitHub WebHook 使用 Secret 對 Post Body 內容進行哈希(HMAC 十六進制摘要)計算,得到一個哈希值,如 xxyyzz

  2. 2. 將哈希值存入請求頭 X-Hub-Signature-256 中,值以 sha256= 開頭,如 sha256=xxyyzz

    1. Web 服務收到請求,使用相同的 Secret 對 Post Body 進行相同哈希算法計算。得到一個摘要。
  3. 4. 取出 X-Hub-Signature-256 請求頭的值進行比較,如果相同則表示請求來自 GitHub Web Hook。

下面是官方給出的 JavaScript 語言的驗證實現:

let encoder = new TextEncoder();

async function verifySignature(secret, header, payload) {
    let parts = header.split("=");
    let sigHex = parts[1];

    let algorithm = { name: "HMAC", hash: { name: 'SHA-256' } };

    let keyBytes = encoder.encode(secret);
    let extractable = false;
    let key = await crypto.subtle.importKey(
        "raw",
        keyBytes,
        algorithm,
        extractable,
        [ "sign""verify" ],
    );

    let sigBytes = hexToBytes(sigHex);
    let dataBytes = encoder.encode(payload);
    let equal = await crypto.subtle.verify(
        algorithm.name,
        key,
        sigBytes,
        dataBytes,
    );

    return equal;
}

function hexToBytes(hex) {
    let len = hex.length / 2;
    let bytes = new Uint8Array(len);

    let index = 0;
    for (let i = 0; i < hex.length; i += 2) {
        let c = hex.slice(i, i + 2);
        let b = parseInt(c, 16);
        bytes[index] = b;
        index += 1;
    }

    return bytes;
}

這種對內容進行摘要計算的驗證方式其實很常見,在之前介紹過的 JWT 的原理中也有提到,感興趣的可以查看:JSON Web Token 入門教程 [1]

注意:Secret 十分重要,應該妥善保存,防止泄漏。更不要存儲到公開倉庫之中。要天不知地不知,GitHub 知你知。

Java 驗證 WebHook

網上有很多使用 Java 語言驗證 GitHub WebHook 消息的代碼實現,這裏使用第三方依賴進行驗證,省去哈希算法的編寫。

引入依賴:

<dependency>
    <groupId>am.ik.webhook</groupId>
    <artifactId>webhook-verifier</artifactId>
    <version>0.1.2</version>
</dependency>

消息驗證:

private void verify(HttpServletRequest request, String body) {
    HmacWebhookSigner webhookSigner = new HmacWebhookSigner("SHA256", secret);
    WebhookVerifier verifier = new WebhookVerifier(webhookSigner, WebhookSigner.Encoder.HEX);
    String signature = request.getHeader(WebhookHttpHeaders.X_HUB_SIGNATURE_256);
    verifier.verify(body, signature);
}

驗證通過沒有任何返回,如果驗證失敗,會拋出 WebhookAuthenticationException 異常。

WebHook 最佳實踐

遵循 WebHook 最佳實踐可以提高其安全性和性能,下面是一些常用建議。

    1. 只訂閱關注的事件,減少事件推送次數。
    1. 使用 HTTPS 提高安全性,HTTPS 已經是 Web 服務的標配。
    1. 配置白名單或驗證策略,確保消息發送方可信,比如文中提到的祕鑰哈希驗證。
    1. 快速響應請求,很多 WebHook 推送對響應耗時有要求,比如 GitHub 是 10 秒,因此如果你的處理邏輯過於耗時,可以考慮異步處理,優先響應。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/pBU-XiI7528qPR1Px46kvw