我们经常发布文档更新,此页面的翻译可能仍在进行中。有关最新信息,请访问英文文档。如果此页面上的翻译有问题,请告诉我们

此版本的 GitHub Enterprise 已停止服务 2020-11-12. 即使针对重大安全问题,也不会发布补丁。 要获得更好的性能、改进的安全性和新功能,请升级到 GitHub Enterprise 的最新版本。 如需升级方面的帮助,请联系 GitHub Enterprise 支持

Securing your webhooks

Ensure your server is only receiving the expected GitHub requests for security reasons.

本文内容

Once your server is configured to receive payloads, it'll listen for any payload sent to the endpoint you configured. For security reasons, you probably want to limit requests to those coming from GitHub. There are a few ways to go about this--for example, you could opt to allow requests from GitHub's IP address--but a far easier method is to set up a secret token and validate the information.

您可以使用仓库、组织和应用 web 挂钩 REST API 来创建、更新、删除和 ping web 挂钩。 更多信息请参阅:

Setting your secret token

You'll need to set up your secret token in two places: GitHub and your server.

To set your token on GitHub:

  1. Navigate to the repository where you're setting up your webhook.
  2. Fill out the Secret textbox. Use a random string with high entropy (e.g., by taking the output of ruby -rsecurerandom -e 'puts SecureRandom.hex(20)' at the terminal).
    Webhook secret token field
  3. Click Update Webhook.

Next, set up an environment variable on your server that stores this token. Typically, this is as simple as running:

$ export SECRET_TOKEN=your_token

Never hardcode the token into your app!

Validating payloads from GitHub

When your secret token is set, GitHub Enterprise Server uses it to create a hash signature with each payload. This hash signature is included with the headers of each request as X-Hub-Signature.

For example, if you have a basic server that listens for webhooks, it might be configured similar to this:

require 'sinatra'
require 'json'

post '/payload' do
  push = JSON.parse(params[:payload])
  "I got some JSON: #{push.inspect}"
end

The intention is to calculate a hash using your SECRET_TOKEN, and ensure that the result matches the hash from GitHub Enterprise Server. GitHub Enterprise Server uses an HMAC hex digest to compute the hash, so you could reconfigure your server to look a little like this:

post '/payload' do
  request.body.rewind
  payload_body = request.body.read
  verify_signature(payload_body)
  push = JSON.parse(params[:payload])
  "I got some JSON: #{push.inspect}"
end

def verify_signature(payload_body)
  signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body)
  return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE'])
end

Your language and server implementations may differ from this example code. However, there are a number of very important things to point out:

  • No matter which implementation you use, the hash signature starts with sha256=, using the key of your secret token and your payload body.

  • Using a plain == operator is not advised. A method like secure_compare performs a "constant time" string comparison, which helps mitigate certain timing attacks against regular equality operators.