Skip to content

Commit

Permalink
update security page and move webhook index to an overview page
Browse files Browse the repository at this point in the history
  • Loading branch information
allenheltondev committed Jan 10, 2024
1 parent f63bf91 commit 916c8e7
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 138 deletions.
2 changes: 1 addition & 1 deletion docs/topics/develop/api-reference/webhooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ The `WebhookDestination` object contains the information about where the webhook

<details>
<summary>How do I validate incoming webhook requests?</summary>
Check out our <a href="../guides/validating-inbound-webhook-requests">dedicated guide</a> on validating inbound webhook requests.
Check out our [webhook security page](./../../webhooks/security.md) on validating inbound webhook requests.
</details>

<details>
Expand Down
130 changes: 0 additions & 130 deletions docs/topics/develop/guides/validating-inbound-webhook-requests.md

This file was deleted.

9 changes: 9 additions & 0 deletions docs/topics/webhooks/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"label": "Webhooks",
"position": 2,
"collapsible": true,
"collapsed": true,
"link": {
"description": "Learn about Webhooks, an event-driven mechanism to listen to Momento Topics"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ title: Webhooks
description: Learn about Webhooks, an event-driven mechanism to listen to Momento Topics
image: https://assets-global.website-files.com/628fadb065a50abf13a11485/659580dc80a9699d3d1ae72a_webhooks-launch-blog-header.png
hide_title: true
sidebar_position: 3
sidebar_label: Webhooks
sidebar_position: 1
sidebar_label: Overview
pagination_prev: null
keywords:
- topics
Expand Down Expand Up @@ -74,7 +74,7 @@ Message number for the topic. As messages are published, the `topic_sequence_num

#### token_id

Unique identifier from a [token](./../develop/authentication/tokens.md). When a token is created via the Momento `AuthClient`, you can provide an identifier to be passed along all events published to Momento Topics. [Learn more](https://www.gomomento.com/blog/momento-topics-just-got-more-secure-introducing-embedded-token-identifiers).
Unique identifier from a [token](./topics/develop/authentication/tokens). When a token is created via the Momento `AuthClient`, you can provide an identifier to be passed along all events published to Momento Topics. [Learn more](https://www.gomomento.com/blog/momento-topics-just-got-more-secure-introducing-embedded-token-identifiers).

#### text

Expand All @@ -86,9 +86,10 @@ Each request includes two specific headers provided by Momento, enabling integra

#### Momento-Signature

A signature of the request payload, signed with the secret you specified when setting up your webhook, used to verify the integrity and origin of the request. Check out our [dedicated guide](../topics/develop/guides/validating-inbound-webhook-requests) on validating inbound webhook requests.
A signature of the request payload, signed with the secret you specified when setting up your webhook, used to verify the integrity and origin of the request. Check out our [webhook security page](./security.md) on validating inbound requests.

#### User-Agent

A static header sent by Momento to identify the source of the request, with the format `Momento/1.0 (Webhooks; +https://docs.momentohq.com/topics/webhooks)`.

## Event delivery
Expand All @@ -100,11 +101,11 @@ A failed delivery, which will not be redriven automatically, could be due to one
* A non-2XX response (e.g., 400, 403, 500, etc.).
* No response after 5 seconds.
* A connection error to your endpoint.
* Rate limit exceeded ([see default limits](./../limits.md)).
* Rate limit exceeded ([see default limits](./topics/limits)).

## API calls for Webhooks

Interested in using our SDKs to programmatically setup webhooks? See our [API reference page for Webhooks](./develop/api-reference/webhooks).
Interested in using our SDKs to programmatically setup webhooks? See our [API reference page for webhooks](./topics/develop/api-reference/webhooks).

## See More

Expand Down
136 changes: 136 additions & 0 deletions docs/topics/webhooks/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
sidebar_position: 2
sidebar_label: Security
title: Webhook Security
description: Learn how to secure your endpoint accepting webhook requests with request signatures and verification.
pagination_next: null
hide_title: true
keywords:
- momento
- webhooks
- security
- eda
- event-driven architectures
- serverless
---

# Secure your webhook endpoints

After you successfully connect a webhook to Momento, it is recommended to validate incoming requests to guarantee the authenticity. Since your endpoint is publicly accessible, guaranteeing the sender is critical in protecting against bad actors. Authenticity and integrity of the requests can be validated in a couple of ways.

## Request signing

Request signing is a security measure used in software systems to verify the authenticity and integrity of a message. Prior to publishing, Momento signs webhook events with a signature contained in the request's `momento-signature` header. This signature uses your webhook's [signing secret](#signing-secret) and event request body to confirm authenticity. This allows you to verify the event originated from Momento, not from a 3rd party or malicious actor.

### Verifying signatures with the Momento SDK

The Momento [Node.js SDK](./../develop/sdks/nodejs) includes native support for verifying signatures. This is the recommended approach to verify signatures. To verify, provide the raw request body, the signature, and your signing secret.

```javascript
import express from 'express';
import { WebhookUtils } from '@gomomento/sdk';
const app = express();

app.post('/', (req, res) => {
const response = WebhookUtils.validateWebhookRequest({
body: req.rawBody,
signature: req.headers['momento-signature'],
signingSecret: process.env.SIGNING_SECRET
});

if (response === WebhookUtils.RequestValidation.VALID) {
// Request is valid. Continue processing.
} else {
res.status(403).send('Signature invalid');
}
});
```

### Verifying signatures manually

For other languages, or if you prefer to verify the signature yourself, you can use an *HMAC SHA3-256* with your signing secret to validate the incoming request.

```javascript
import * as crypto from 'crypto';

const didRequestComeFromMomento = (req, secret) => {
const hash = crypto.createHmac("SHA3-256", secret);
const hashed = hash.update(req.rawBody).digest('hex');
return hashed === req.headers['momento-signature'];
};
```

## User Agent

In addition to the digital signature, all webhook events also receive a `User-Agent` header. This can be used to in conjunction with the signature to guarantee authenticity or used to divert the workflow for testing scenarios. This header is a static value that only changes on major version releases:

`Momento/1.0 (Webhooks; +https://docs.momentohq.com/topics/webhooks)`

The value is composed of the following pieces:

* **Momento** - Indicates the origin is from Momento
* **1.0** - Major version number of the service
* **Webhooks** - States the specific origin from the request
* **+https://docs.momentohq.com/topics/webhooks** - Additional information about the webhook service can be found here

:::warning

Do not exlusively use this value to validate incoming requests. This header is easily spoofed and is not intended to verify authenticity on its own.

:::

### Running test scenarios

Before bringing your app to production, you might want to test your webhook is in working order and you can verify requests without executing your production code. This is where the `User-Agent` header can be used to divert execution of your code. By creating a request, signing it yourself using the [manual verification code](#verifying-signatures-manually), and publishing to your endpoint with a different `User-Agent` header value, you can shortcut the execution after verifying the payload.

```javascript
const MOMENTO_USER_AGENT = 'Momento/1.0 (Webhooks; +https://docs.momentohq.com/topics/webhooks)';
const TEST_USER_AGENT = 'Test Harness/PostmanRuntime';

app.post('/', (req, res) => {
if(didRequestComeFromMomento(req, process.env.SIGNING_SECRET)){
if(req.headers['User-Agent'] === MOMENTO_USER_AGENT){
// Continue processing
} else if (req.headers['User-Agent'] === TEST_USER_AGENT) {
// This is a test request, return success
res.status(200).send('Skipping execution');
}
} else {
res.status(403).send('Signature invalid');
}
});
```

## Signing secret

When you create a new webhook in Momento you will be provided a signing secret. This secret value is used to digitally sign event payloads from Momento, allowing you to verify authenticity of a request. You can obtain the secret in either programmatically or via the [Momento console](https://console.gomomento.com);

### Obtaining your signing secret programmatically

When you create a new webhook with the [putWebhook API](./../develop/api-reference/webhooks#put-webhook-api), you will receive the signing secret as part of a successful response. If you lose the secret or forget to save it as part of the request, the [getWebhookSecret API](./../develop/api-reference/webhooks#get-webhook-secret-api) will return the current value.

### Obtaining your signing secret via the Momento console

If you prefer using the Momento console, you can always navigate to your webhook and click on the *copy* button next on the secret string row.

![Webhook detail page in the Momento Console](@site/static/img/webhook-secret-string.png)

To navigate to the webhook detail page, click on the cache of your choice on the [cache list page](https://console.gomomento.com/caches). Navigate to the *Webhooks* menu option and click on the webhook you wish to view.

:::tip
When you first create a webhook you will be navigated to the webhook list page. To retrieve the signing secret, you must then click into the webhook and press the copy button.
:::

## Considerations

The verification information listed above is the minimum required to guarantee authenticity of an incoming request. However, further security best practices should also be considered when building your webhook.

### Replay attacking

If a valid webhook event is intercepted by a malicious actor and your endpoint implements sender verification only, you are susceptible to a *replay attack*. A replay attack occurs when a bad actor sends a valid request to your system repeatedly, forcing your system to process the same event multiple times. To prevent these attacks from harming your system, consider also validating the age of the event. Any event that comes in older than your allowed age threshold would be automatically discarded.

You can use the [publish_timestamp](./index#publish_timestamp) property of the event to determine age. As best practice, consider rejecting events *older than 60 seconds*.

### Regularly rotate secrets

If your webhook signing secret is compromised, you cannot guarantee authenticity of a request. Consider rotating the secret periodically to ensure safety of your endpoints. You can rotate a secret using the [rotateWebhookSecret API](./../develop/api-reference/webhooks#rotate-webhook-secret-api).
4 changes: 4 additions & 0 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ const config = {
{
from: '/develop/sdks/nodejs/topics-cheat-sheet',
to: '/topics/develop/sdks/nodejs/cheat-sheet'
},
{
from: '/topics/webhooks',
to: '/topics/webhooks/overview'
}
],
// This came in with v1.5.0 of the docs where we split out by service.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ Slackへの投稿、データレイクへの送信、データベースエント

## WebhooksのAPIコール

[WebhooksのAPIリファレンスページ](./develop/api-reference/webhooks)を参照してください。
[WebhooksのAPIリファレンスページ](./../develop/api-reference/webhooks)を参照してください。
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
sidebar_position: 2
sidebar_label: Security
title: Webhook Security
description: Learn how to secure your endpoint accepting webhook requests with request signatures and verification.
pagination_next: null
hide_title: true
keywords:
- momento
- webhooks
- security
- eda
- event-driven architectures
- serverless
---
Binary file added static/img/webhook-secret-string.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 916c8e7

Please sign in to comment.