From fb0e233dd76fc9e38a8f3c25dfbd0d40e7deafc9 Mon Sep 17 00:00:00 2001 From: Ian Roberts Date: Sun, 27 Oct 2024 20:57:57 +0000 Subject: [PATCH] docs: documentation of the new "check-signature" rule --- docs/Hook-Examples.md | 14 ++-- docs/Hook-Rules.md | 163 +++++++++++++++++++++++------------------- 2 files changed, 96 insertions(+), 81 deletions(-) diff --git a/docs/Hook-Examples.md b/docs/Hook-Examples.md index 7d07932..cff1818 100644 --- a/docs/Hook-Examples.md +++ b/docs/Hook-Examples.md @@ -25,7 +25,7 @@ although the examples on this page all use the JSON format. ## Incoming Github webhook -This example works on 2.8+ versions of Webhook - if you are on a previous series, change `payload-hmac-sha1` to `payload-hash-sha1`. +This example works on 2.9+ versions of Webhook - if you are on a previous series, change the `check-signature` block to an equivalent `match` block, see the [Hook-Rules](Hook-Rules.md#legacy-match-rules-for-signatures) page for full details. ```json [ @@ -53,11 +53,11 @@ This example works on 2.8+ versions of Webhook - if you are on a previous series "and": [ { - "match": + "check-signature": { - "type": "payload-hmac-sha1", + "algorithm": "sha1", "secret": "mysecret", - "parameter": + "signature": { "source": "header", "name": "X-Hub-Signature" @@ -185,11 +185,11 @@ Values in the request body can be accessed in the command or to the match rule b "and": [ { - "match": + "check-signature": { - "type": "payload-hmac-sha256", + "algorithm": "sha256", "secret": "mysecret", - "parameter": + "signature": { "source": "header", "name": "X-Gogs-Signature" diff --git a/docs/Hook-Rules.md b/docs/Hook-Rules.md index 84914ca..ad3cdac 100644 --- a/docs/Hook-Rules.md +++ b/docs/Hook-Rules.md @@ -9,11 +9,12 @@ * [Match](#match) * [Match value](#match-value) * [Match regex](#match-regex) + * [Match Whitelisted IP range](#match-whitelisted-ip-range) + * [Match scalr-signature](#match-scalr-signature) +* [Check signature](#check-signature) * [Match payload-hmac-sha1](#match-payload-hmac-sha1) * [Match payload-hmac-sha256](#match-payload-hmac-sha256) * [Match payload-hmac-sha512](#match-payload-hmac-sha512) - * [Match Whitelisted IP range](#match-whitelisted-ip-range) - * [Match scalr-signature](#match-scalr-signature) ## And *And rule* will evaluate to _true_, if and only if all of the sub rules evaluate to _true_. @@ -183,78 +184,6 @@ For the regex syntax, check out } ``` -### Match payload-hmac-sha1 -Validate the HMAC of the payload using the SHA1 hash and the given *secret*. -```json -{ - "match": - { - "type": "payload-hmac-sha1", - "secret": "yoursecret", - "parameter": - { - "source": "header", - "name": "X-Hub-Signature" - } - } -} -``` - -Note that if multiple signatures were passed via a comma separated string, each -will be tried unless a match is found. For example: - -``` -X-Hub-Signature: sha1=the-first-signature,sha1=the-second-signature -``` - -### Match payload-hmac-sha256 -Validate the HMAC of the payload using the SHA256 hash and the given *secret*. -```json -{ - "match": - { - "type": "payload-hmac-sha256", - "secret": "yoursecret", - "parameter": - { - "source": "header", - "name": "X-Signature" - } - } -} -``` - -Note that if multiple signatures were passed via a comma separated string, each -will be tried unless a match is found. For example: - -``` -X-Hub-Signature: sha256=the-first-signature,sha256=the-second-signature -``` - -### Match payload-hmac-sha512 -Validate the HMAC of the payload using the SHA512 hash and the given *secret*. -```json -{ - "match": - { - "type": "payload-hmac-sha512", - "secret": "yoursecret", - "parameter": - { - "source": "header", - "name": "X-Signature" - } - } -} -``` - -Note that if multiple signatures were passed via a comma separated string, each -will be tried unless a match is found. For example: - -``` -X-Hub-Signature: sha512=the-first-signature,sha512=the-second-signature -``` - ### Match Whitelisted IP range The IP can be IPv4- or IPv6-formatted, using [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_blocks). To match a single IP address only, use `/32`. @@ -286,3 +215,89 @@ Given the time check make sure that NTP is enabled on both your Scalr and webhoo } } ``` + +## Check Signature + +Many webhook protocols involve the hook sender computing an [HMAC](https://en.wikipedia.org/wiki/HMAC) _signature_ over the request content using a shared secret key, and sending the expected signature value as part of the webhook call. The webhook recipient can then compute their own value for the signature using the same secret key and verify that value against the one supplied by the sender. Since the sender and receiver are (or at least _should be_) the only parties that have knowledge of the secret, a matching signature guarantees that the payload is valid and was created by the legitimate sender. + +The `"check-signature"` rule type is used to validate these kinds of signatures. In its simplest form you just specify the _algorithm_ (`sha1`, `sha256` or `sha512`), the _secret_, and where in the request to find the signature (typically a header or a query parameter). Webhook will compute the HMAC over the whole of the request body using the supplied secret, and compare the result to the one taken from the request + +```json +{ + "check-signature": + { + "algorithm": "sha256", + "secret": "yoursecret", + "signature": + { + "source": "header", + "name": "X-Hub-Signature" + } + } +} +``` + +Note that if multiple signatures were passed via a comma separated string, each +will be tried unless a match is found, and any `algorithm=` prefix is stripped off +each signature value before comparison. This allows for cases where the sender includes +several signatures with different algorithms in the same header, e.g.: + +``` +X-Hub-Signature: sha1=the-sha1-signature,sha256=the-sha256-signature +``` + +If the sender computes the signature over something other than just the request body then you can optionally provide a `"string-to-sign"` argument. Usually this will be a template that assembles the string-to-sign from different parts of the request (one of which could be the body). For example this would compute a signature over the values of the `X-Request-Id` header, `Date` header, and request body, separated by line breaks: + +```yaml +check-signature: + algorithm: sha512 + secret: 5uper5eecret + signature: + source: header + name: X-Hook-Signature + string-to-sign: + source: template + name: | + {{- printf "%s\r\n" (.GetHeader "x-request-id") -}} + {{- printf "%s\r\n" (.GetHeader "date") -}} + {{- .BodyText -}} +``` + +Note that signature algorithms can be very particular about whether "line breaks" are unix style LF or Windows-style CR+LF. It is safest to be explicit, as in the above example, using `{{- -}}` blocks (that ignore the white space within the template itself either side of the block) and `printf` with `\n` or `\r\n`, to ensure the template generates the correct style of line endings whatever platform you created it on. + +### Legacy "match" rules for signatures +In previous versions of webhook signature verification was handled by a set of specific "match" rule types named `payload-hmac-` - the legacy format is still understood but you may wish to update your existing configurations to the new format. + +The legacy configuration + +```json +{ + "match": + { + "type": "payload-hmac-", + "secret": "secret", + "parameter": + { + "source": "header", + "name": "X-Signature" + } + } +} +``` + +is equivalent to the new style + +```json +{ + "check-signature": + { + "algorithm": "", + "secret": "secret", + "signature": + { + "source": "header", + "name": "X-Signature" + } + } +} +```