🔑

GitHub Webhookのシークレットトークンを使用したリクエスト検証方法

2023/02/03に公開

GitHub Webhookを使用する際、肝心なのはセキュリティです。
本当にGitHub Webhookからのリクエストなのか検証する必要があります。

今回はシークレットトークンを使用したリクエスト検証の方法を解説します。

ハッシュ署名を利用する

公式にもある通り、GitHubはシークレットトークンとペイロードを使用してハッシュ署名を作成します。

シークレットトークンが設定されると、GitHub はそれを使用して各ペイロードでハッシュ署名を作成します。 このハッシュ署名は、x-hub-signature-256 として各要求のヘッダーに含まれています。
https://docs.github.com/ja/developers/webhooks-and-events/webhooks/securing-your-webhooks#github-からのペイロードを検証する

つまり、SECRET_TOKENを用いたハッシュ計算とGitHubのハッシュ署名が一致するようにします。

シークレットトークンを設定する

検証するために、まずWebhook設定時にSecretを記述します。

ここのシークレットトークンはなんでもよいのですが、なるべく複雑なものがいいです。
公式の通り、rubyコマンドを使用して生成してみましょう。

ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'

ペイロードを受け取るコード

ここではAWS LambdaでNode.jsを使用する場合を想定します。
環境変数に先ほど設定したSecretSECRET_TOKENとして設定してください。

cryptoを使用し、シークレットトークンとペイロード本文を合わせたハッシュ署名を作成します。そこからGitHubヘッダーにあるx-hub-signature-256の値を照合しています。

index.mjs
import crypto from 'crypto'

const isValidSignature = (body, headers) => {
  const hmac = crypto.createHmac('sha256', process.env.SECRET_TOKEN)
  hmac.update(body, 'utf8')
  const signature = `sha256=${hmac.digest('hex')}`
  return signature === headers['x-hub-signature-256']
}

export const handler = async(event) => {
  const headers = event.headers

  if (!isValidSignature(event.body, headers)) {
    return {
      statusCode: 500,
      body: {
        message: 'signature is invalid'
      }
    }
  }

  return {
    statusCode: 200,
    body: {
      message: 'ok'
    }
  }
}

ちなみにx-hub-signatureも使用できますが、SHA-1を用いているため非推奨になっています。

注: 下位互換性のために、SHA-1 ハッシュ関数を使用して生成される x-hub-signature ヘッダーも含まれています。 可能であれば、セキュリティを強化するために x-hub-signature-256 ヘッダーを使用することをお勧めします。
https://docs.github.com/ja/developers/webhooks-and-events/webhooks/securing-your-webhooks#github-からのペイロードを検証する

さいごに

便利なWebhook機能とはいえ、セキュリティ面で対応は欠かせません。
安全にリクエスト許可できるよう気をつけて設定していきましょう。

Discussion