Skip to content

Commit

Permalink
repo sync
Browse files Browse the repository at this point in the history
  • Loading branch information
Octomerger authored Oct 14, 2020
2 parents 9b140ba + d9a23d5 commit d516858
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 12 deletions.
27 changes: 20 additions & 7 deletions content/developers/webhooks-and-events/securing-your-webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,17 @@ $ export SECRET_TOKEN=<em>your_token</em>

### Validating payloads from GitHub

When your secret token is set, GitHub uses it to create a hash signature with each payload.
When your secret token is set, {% data variables.product.product_name %} uses it to create a hash signature with each payload. This hash signature is included with the headers of each request as {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or currentVersion == "private-instances@latest" %}`X-Hub-Signature-256`{% else if currentVersion ver_lt "[email protected]" %}`X-Hub-Signature`{% endif %}.

This hash signature is passed along with each request in the headers as `X-Hub-Signature`. Suppose you have a basic server listening to webhooks that looks like this:
{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or currentVersion == "private-instances@latest" %}
{% note %}

**Note:** For backward-compatibility, we also include the `X-Hub-Signature` header that is generated using the SHA-1 hash function. If possible, we recommend that you use the `X-Hub-Signature-256` header for improved security. The example below demonstrate using the `X-Hub-Signature-256` header.

{% endnote %}
{% endif %}

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

``` ruby
require 'sinatra'
Expand All @@ -48,7 +56,7 @@ post '/payload' do
end
```

The goal is to compute a hash using your `SECRET_TOKEN`, and ensure that the hash from GitHub matches. GitHub uses an HMAC hexdigest to compute the hash, so you could change your server to look a little like this:
The intention is to calculate a hash using your `SECRET_TOKEN`, and ensure that the result matches the hash from {% data variables.product.product_name %}. {% data variables.product.product_name %} uses an HMAC hex digest to compute the hash, so you could reconfigure your server to look a little like this:

``` ruby
post '/payload' do
Expand All @@ -59,16 +67,21 @@ post '/payload' do
"I got some JSON: #{push.inspect}"
end

{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or currentVersion == "private-instances@latest" %}
def verify_signature(payload_body)
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_2'])
end{% else if currentVersion ver_lt "[email protected]" %}
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
end{% endif %}
```
Obviously, your language and server implementations may differ than this code. There are a couple of very important things to point out, however:
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 `sha1=`, using the key of your secret token and your payload body.
* No matter which implementation you use, the hash signature starts with {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or "private-instances@latest" %}`sha256=`{% else if currentVersion ver_lt "[email protected]" %}`sha1=`{% endif %}, using the key of your secret token and your payload body.

* Using a plain `==` operator is **not advised**. A method like [`secure_compare`][secure_compare] performs a "constant time" string comparison, which renders it safe from certain timing attacks against regular equality operators.
* Using a plain `==` operator is **not advised**. A method like [`secure_compare`][secure_compare] performs a "constant time" string comparison, which helps mitigate certain timing attacks against regular equality operators.

[secure_compare]: http://rubydoc.info/github/rack/rack/master/Rack/Utils.secure_compare
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ Header | Description
`X-GitHub-Event`| Name of the event that triggered the delivery.
`X-GitHub-Delivery`| A [GUID](http://en.wikipedia.org/wiki/Globally_unique_identifier) to identify the delivery.{% if currentVersion != "free-pro-team@latest" %}
`X-GitHub-Enterprise-Version` | The version of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload.
`X-GitHub-Enterprise-Host` | The hostname of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload.{% endif %}
`X-Hub-Signature`| The HMAC hex digest of the response body. This header will be sent if the webhook is configured with a [`secret`](/v3/repos/hooks/#create-hook-config-params). The HMAC hex digest is generated using the `sha1` hash function and the `secret` as the HMAC `key`.
`X-GitHub-Enterprise-Host` | The hostname of the {% data variables.product.prodname_ghe_server %} instance that sent the HTTP POST payload.{% endif %}{% if currentVersion != "private-instances@latest" %}
`X-Hub-Signature`| This header is sent if the webhook is configured with a [`secret`](/v3/repos/hooks/#create-hook-config-params). This is the HMAC hex digest of the request body, and is generated using the SHA-1 hash function and the `secret` as the HMAC `key`.{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" %} `X-Hub-Signature` is provided for compatibility with existing integrations, and we recommend that you use the more secure `X-Hub-Signature-256` instead.{% endif %}{% endif %}{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or currentVersion == "private-instances@latest" %}
`X-Hub-Signature-256`| This header is sent if the webhook is configured with a [`secret`](/v3/repos/hooks/#create-hook-config-params). This is the HMAC hex digest of the request body, and is generated using the SHA-256 hash function and the `secret` as the HMAC `key`.{% endif %}

Also, the `User-Agent` for the requests will have the prefix `GitHub-Hookshot/`.

Expand All @@ -62,8 +63,9 @@ Also, the `User-Agent` for the requests will have the prefix `GitHub-Hookshot/`.
> Host: localhost:4567
> X-GitHub-Delivery: 72d3162e-cc78-11e3-81ab-4c9367dc0958{% if currentVersion != "free-pro-team@latest" %}
> X-GitHub-Enterprise-Version: 2.15.0
> X-GitHub-Enterprise-Host: example.com{% endif %}
> X-Hub-Signature: sha1=7d38cdd689735b008b3c702edd92eea23791c5f6
> X-GitHub-Enterprise-Host: example.com{% endif %}{% if currentVersion != "private-instances@latest" %}
> X-Hub-Signature: sha1=7d38cdd689735b008b3c702edd92eea23791c5f6{% endif %}{% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" or currentVersion == "private-instances@latest" %}
> X-Hub-Signature-256: sha256=d57c68ca6f92289e6987922ff26938930f6e66a2d161ef06abdf1859230aa23c{% endif %}
> User-Agent: GitHub-Hookshot/044aadd
> Content-Type: application/json
> Content-Length: 6615
Expand Down
2 changes: 1 addition & 1 deletion data/reusables/webhooks/secret.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Setting a webhook secret allows you to ensure that `POST` requests sent to the payload URL are from GitHub. When you set a secret, you'll receive the `X-Hub-Signature` header in the webhook `POST` request. For more details on how to use the secret and the `X-Hub-Signature` header to secure your webhook payloads, see "[Securing your webhooks](/webhooks/securing/)."
Setting a webhook secret allows you to ensure that `POST` requests sent to the payload URL are from {% data variables.product.product_name %}. When you set a secret, you'll receive the {% if currentVersion == "free-pro-team@latest" or currentVersion ver_gt "[email protected]" %}`X-Hub-Signature` and `X-Hub-Signature-256` headers{% else if currentVersion ver_lt "[email protected]" %}`X-Hub-Signature` header{% else if currentVersion == "private-instances@latest" %}`X-Hub-Signature-256` header{% endif %} in the webhook `POST` request. For more information on how to use a secret with a signature header to secure your webhook payloads, see "[Securing your webhooks](/webhooks/securing/)."

0 comments on commit d516858

Please sign in to comment.