|
1 | 1 | # Auth Plugins
|
2 | 2 |
|
3 |
| -This document provides an example of how to implement a custom authentication plugin for a hypothetical system. The plugin checks for a specific authorization header and validates it against a secret stored in an environment variable. |
| 3 | +This document provides information on how to use authentication plugins for webhook validation, including built-in plugins and how to implement custom authentication plugins. |
| 4 | + |
| 5 | +In your global configuration file (e.g. `hooks.yml`) you would likely set `auth_plugin_dir` to something like `./plugins/auth`. |
| 6 | + |
| 7 | +Here is an example snippet of how you might configure the global settings in `hooks.yml`: |
| 8 | + |
| 9 | +```yaml |
| 10 | +# hooks.yml |
| 11 | +auth_plugin_dir: ./plugins/auth # Directory where custom auth plugins are stored |
| 12 | +``` |
| 13 | +
|
| 14 | +## Built-in Auth Plugins |
| 15 | +
|
| 16 | +The system comes with several built-in authentication plugins that cover common webhook authentication patterns. |
| 17 | +
|
| 18 | +### HMAC Authentication |
| 19 | +
|
| 20 | +The HMAC plugin provides secure signature-based authentication using HMAC (Hash-based Message Authentication Code). This is the most secure authentication method and is used by major webhook providers like GitHub, GitLab, and Shopify. |
| 21 | +
|
| 22 | +It works well because it HMACs provide the ability to verify both the integrity and authenticity of the request, ensuring that the payload has not been tampered with and that it comes from a trusted source. |
| 23 | +
|
| 24 | +**Type:** `hmac` |
| 25 | + |
| 26 | +#### HMAC Configuration Options |
| 27 | + |
| 28 | +##### `secret_env_key` (required) |
| 29 | + |
| 30 | +The name of the environment variable containing the shared secret used for HMAC signature generation. |
| 31 | + |
| 32 | +**Example:** `GITHUB_WEBHOOK_SECRET` |
| 33 | + |
| 34 | +##### `header` |
| 35 | + |
| 36 | +The HTTP header containing the HMAC signature. |
| 37 | + |
| 38 | +**Default:** `X-Signature` |
| 39 | +**Example:** `X-Hub-Signature-256` |
| 40 | + |
| 41 | +##### `algorithm` |
| 42 | + |
| 43 | +The hashing algorithm to use for HMAC signature generation. |
| 44 | + |
| 45 | +**Default:** `sha256` |
| 46 | +**Valid values:** `sha1`, `sha256`, `sha384`, `sha512` |
| 47 | +**Example:** `sha256` |
| 48 | + |
| 49 | +##### `format` |
| 50 | + |
| 51 | +The format of the signature in the header. This determines how the signature is structured. |
| 52 | + |
| 53 | +**Default:** `algorithm=signature` |
| 54 | + |
| 55 | +**Valid values:** |
| 56 | + |
| 57 | +- `algorithm=signature` - Produces "sha256=abc123..." (GitHub, GitLab style) |
| 58 | +- `signature_only` - Produces "abc123..." (Shopify style) |
| 59 | +- `version=signature` - Produces "v0=abc123..." (Slack style) |
| 60 | + |
| 61 | +##### `version_prefix` |
| 62 | + |
| 63 | +The version prefix used when `format` is set to `version=signature`. |
| 64 | + |
| 65 | +**Default:** `v0` |
| 66 | +**Example:** `v1` |
| 67 | + |
| 68 | +##### `timestamp_header` (optional) |
| 69 | + |
| 70 | +The HTTP header containing the request timestamp for timestamp validation. When specified, requests must include a valid timestamp within the tolerance window. |
| 71 | + |
| 72 | +**Example:** `X-Request-Timestamp` |
| 73 | + |
| 74 | +##### `timestamp_tolerance` |
| 75 | + |
| 76 | +The maximum age (in seconds) allowed for timestamped requests. Only used when `timestamp_header` is specified. |
| 77 | + |
| 78 | +**Default:** `300` (5 minutes) |
| 79 | +**Example:** `600` |
| 80 | + |
| 81 | +##### `payload_template` (optional) |
| 82 | + |
| 83 | +A template for constructing the payload used in signature generation when timestamp validation is enabled. Use placeholders like `{version}`, `{timestamp}`, and `{body}`. |
| 84 | + |
| 85 | +**Example:** `{version}:{timestamp}:{body}` |
| 86 | + |
| 87 | +#### HMAC Examples |
| 88 | + |
| 89 | +**Basic GitHub-style HMAC:** |
| 90 | + |
| 91 | +```yaml |
| 92 | +auth: |
| 93 | + type: hmac |
| 94 | + secret_env_key: GITHUB_WEBHOOK_SECRET |
| 95 | + header: X-Hub-Signature-256 |
| 96 | + algorithm: sha256 |
| 97 | + format: "algorithm=signature" # produces "sha256=abc123..." |
| 98 | +``` |
| 99 | + |
| 100 | +**Shopify-style HMAC (signature only):** |
| 101 | + |
| 102 | +```yaml |
| 103 | +auth: |
| 104 | + type: hmac |
| 105 | + secret_env_key: SHOPIFY_WEBHOOK_SECRET |
| 106 | + header: X-Shopify-Hmac-Sha256 |
| 107 | + algorithm: sha256 |
| 108 | + format: "signature_only" # produces "abc123..." |
| 109 | +``` |
| 110 | + |
| 111 | +**Slack-style HMAC with timestamp validation:** |
| 112 | + |
| 113 | +This is the most secure authentication method as it includes timestamp validation directly in the HMAC signature, preventing replay attacks even if an attacker intercepts the request. |
| 114 | + |
| 115 | +```yaml |
| 116 | +auth: |
| 117 | + type: hmac |
| 118 | + secret_env_key: SLACK_WEBHOOK_SECRET |
| 119 | + header: X-Slack-Signature |
| 120 | + timestamp_header: X-Slack-Request-Timestamp |
| 121 | + timestamp_tolerance: 300 # 5 minutes |
| 122 | + algorithm: sha256 |
| 123 | + format: "version=signature" # produces "v0=abc123..." |
| 124 | + version_prefix: "v0" |
| 125 | + payload_template: "{version}:{timestamp}:{body}" |
| 126 | +``` |
| 127 | + |
| 128 | +**Security Benefits:** |
| 129 | + |
| 130 | +The timestamp validation provides several critical security advantages: |
| 131 | + |
| 132 | +1. **Replay Attack Prevention**: Even if an attacker captures a valid request, they cannot replay it after the timestamp tolerance window expires |
| 133 | +2. **HMAC Integrity**: The timestamp is included in the HMAC calculation itself (via `payload_template`), so tampering with either the timestamp or payload will invalidate the signature |
| 134 | +3. **Time-bound Validity**: Requests are only valid within a specific time window, reducing the attack surface |
| 135 | + |
| 136 | +**How it works:** |
| 137 | + |
| 138 | +1. The client includes the current Unix timestamp in the `X-Slack-Request-Timestamp` header |
| 139 | +2. The HMAC is calculated over a constructed payload using the template: `{version}:{timestamp}:{body}` |
| 140 | +3. For example, if the version is "v0", timestamp is "1609459200", and body is `{"event":"push"}`, the signed payload becomes: `v0:1609459200:{"event":"push"}` |
| 141 | +4. The resulting signature format is: `v0=computed_hmac_hash` |
| 142 | + |
| 143 | +**Example curl request:** |
| 144 | + |
| 145 | +```bash |
| 146 | +#!/bin/bash |
| 147 | +
|
| 148 | +# Configuration |
| 149 | +WEBHOOK_URL="https://your-hooks-server.com/webhooks/slack" |
| 150 | +SECRET="your_slack_webhook_secret" |
| 151 | +TIMESTAMP=$(date +%s) |
| 152 | +PAYLOAD='{"event":"push","repository":"my-repo"}' |
| 153 | +
|
| 154 | +# Construct the signing payload |
| 155 | +VERSION="v0" |
| 156 | +SIGNING_PAYLOAD="${VERSION}:${TIMESTAMP}:${PAYLOAD}" |
| 157 | +
|
| 158 | +# Generate HMAC signature |
| 159 | +SIGNATURE=$(echo -n "$SIGNING_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex | cut -d' ' -f2) |
| 160 | +FORMATTED_SIGNATURE="${VERSION}=${SIGNATURE}" |
| 161 | +
|
| 162 | +# Send the request |
| 163 | +curl -X POST "$WEBHOOK_URL" \ |
| 164 | + -H "Content-Type: application/json" \ |
| 165 | + -H "X-Slack-Signature: $FORMATTED_SIGNATURE" \ |
| 166 | + -H "X-Slack-Request-Timestamp: $TIMESTAMP" \ |
| 167 | + -d "$PAYLOAD" |
| 168 | +``` |
| 169 | + |
| 170 | +**Important Security Notes:** |
| 171 | + |
| 172 | +- The timestamp must be included in the HMAC calculation (not just validated separately) to prevent signature reuse with different timestamps |
| 173 | +- Use a reasonable `timestamp_tolerance` (5-10 minutes) to account for clock skew while minimizing replay window |
| 174 | +- Always use HTTPS to prevent man-in-the-middle attacks |
| 175 | +- Store webhook secrets securely |
| 176 | + |
| 177 | +**General HMAC with timestamp validation (no version):** |
| 178 | + |
| 179 | +For services that require timestamp validation but don't use version prefixes, you can use a simpler template format with the standard `algorithm=signature` format. |
| 180 | + |
| 181 | +```yaml |
| 182 | +auth: |
| 183 | + type: hmac |
| 184 | + secret_env_key: WEBHOOK_SECRET |
| 185 | + header: X-Signature |
| 186 | + timestamp_header: X-Timestamp |
| 187 | + timestamp_tolerance: 600 # 10 minutes |
| 188 | + algorithm: sha256 |
| 189 | + format: "algorithm=signature" # produces "sha256=abc123..." |
| 190 | + payload_template: "{timestamp}:{body}" |
| 191 | +``` |
| 192 | + |
| 193 | +**Example curl request:** |
| 194 | + |
| 195 | +```bash |
| 196 | +#!/bin/bash |
| 197 | +
|
| 198 | +# Configuration |
| 199 | +WEBHOOK_URL="https://your-hooks-server.com/webhooks/generic" |
| 200 | +SECRET="your_webhook_secret" |
| 201 | +TIMESTAMP=$(date +%s) |
| 202 | +PAYLOAD='{"event":"deployment","status":"success"}' |
| 203 | +
|
| 204 | +# Construct the signing payload (timestamp:body format) |
| 205 | +SIGNING_PAYLOAD="${TIMESTAMP}:${PAYLOAD}" |
| 206 | +
|
| 207 | +# Generate HMAC signature |
| 208 | +SIGNATURE=$(echo -n "$SIGNING_PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -hex | cut -d' ' -f2) |
| 209 | +FORMATTED_SIGNATURE="sha256=${SIGNATURE}" |
| 210 | +
|
| 211 | +# Send the request |
| 212 | +curl -X POST "$WEBHOOK_URL" \ |
| 213 | + -H "Content-Type: application/json" \ |
| 214 | + -H "X-Signature: $FORMATTED_SIGNATURE" \ |
| 215 | + -H "X-Timestamp: $TIMESTAMP" \ |
| 216 | + -d "$PAYLOAD" |
| 217 | +``` |
| 218 | + |
| 219 | +This approach provides strong security through timestamp validation while using a simpler format than the Slack-style implementation. The signing payload becomes `1609459200:{"event":"deployment","status":"success"}` and the resulting signature format is `sha256=computed_hmac_hash`. |
| 220 | + |
| 221 | +### Shared Secret Authentication |
| 222 | + |
| 223 | +The SharedSecret plugin provides simple secret-based authentication by comparing a secret value sent in an HTTP header. While simpler than HMAC, it provides less security since the secret is transmitted directly in the request header. |
| 224 | + |
| 225 | +**Type:** `shared_secret` |
| 226 | + |
| 227 | +#### Shared Secret Configuration Options |
| 228 | + |
| 229 | +##### `secret_env_key` (required for shared secrets) |
| 230 | + |
| 231 | +The name of the environment variable containing the shared secret for validation. |
| 232 | + |
| 233 | +**Example:** `WEBHOOK_SECRET` |
| 234 | + |
| 235 | +##### `header` (contains the shared secret) |
| 236 | + |
| 237 | +The HTTP header where the shared secret is transmitted. |
| 238 | + |
| 239 | +**Default:** `Authorization` |
| 240 | +**Example:** `X-API-Key` |
| 241 | + |
| 242 | +#### Shared Secret Examples |
| 243 | + |
| 244 | +**Basic shared secret with Authorization header:** |
| 245 | + |
| 246 | +```yaml |
| 247 | +auth: |
| 248 | + type: shared_secret |
| 249 | + secret_env_key: WEBHOOK_SECRET |
| 250 | + header: Authorization |
| 251 | +``` |
| 252 | + |
| 253 | +**Custom header shared secret:** |
| 254 | + |
| 255 | +```yaml |
| 256 | +auth: |
| 257 | + type: shared_secret |
| 258 | + secret_env_key: API_KEY_SECRET |
| 259 | + header: X-API-Key |
| 260 | +``` |
| 261 | + |
| 262 | +## Custom Auth Plugins |
| 263 | + |
| 264 | +This section provides an example of how to implement a custom authentication plugin for a hypothetical system. The plugin checks for a specific authorization header and validates it against a secret stored in an environment variable. |
4 | 265 |
|
5 | 266 | In your global configuration file (e.g. `hooks.yml`) you would likely set `auth_plugin_dir` to something like `./plugins/auth`.
|
6 | 267 |
|
|
0 commit comments