将Webhook建立在您的应用程序中:指南和最佳实践(2020)

2021-03-11 00:41:05

如果您的申请正在生成您的客户感兴趣的数据(即,您正在做正确的事情),您将在某些时候为Webhooks获取Lequeed。但是如何建立它们的标准指导,特别是在安全方面。此帖子将通过如何从应用程序发送Webhooks的基础知识,管理身份验证,处理安全性,并为您的客户提供平滑的开发人员体验。

Webhooks是反向API,因此他们需要非标基础设施的基础设施用于构建Webhooks的起点是您的应用程序正在生成客户想要的数据。通常,您可以通过API公开,使用API​​密钥等对您的用户进行身份验证 - 但与Webhooks的差异是您的客户希望主动通知您的应用程序中发生的事情。您的API是为了接收和响应请求,而Webhooks积极向基于内部触发的其他系统发送数据。这需要您持续有关您应该将数据发送到数据的信息以及那些端点的状态。

数据库以存储Webhook将数据发送到[后端]的端点(和关联元数据)

与任何东西一样,这可以像你想要的那样复杂(带有Webhook消费者的Kafka主题)或简单(Lambda)。更稍后的更多信息。

最后,在我们潜入之前,重要的是要定义术语,因为事情可能会令人困惑:

您的应用程序是Webhook Provider - 您正在发送由系统中的事件触发的POST请求

其他开发人员及其应用是消费者 - 他们正在收到您的终点

松弛使用“传出”与“传入”术语,但这个想法是一样的。

使用Webhooks处理身份验证比使用API​​略微棘手,因为您正在向端点发送数据而不接收任何回报 - 有很多方法可能出错(欺骗端点,渗透网络等)。这只是你的最终 - 消费者还需要验证进入其WebHook端点的数据(接受Webhook事件)实际上来自您的应用程序,并且在运输过程中尚未被欺骗/损坏。

您需要做的第一件事是验证登录以订阅WebHook的开发人员实际上拥有他们给您的端点。标准做法是将测试事件发送到端点,并要求开发人员验证他们是否通过返回200,或者包括“挑战”,即端点需要回音。例如,DropBox通过在URL中编码的“挑战”Param(一个随机字符串)发送Get请求来验证WebHook端点,您的端点需要将其作为响应回应。

但是验证端点无法解决整个问题,因为端点仍然可以欺骗,网络不确定等等,基本上有两种方法来解决AUTH问题:

这是避免问题的最简单方法,是Dropbox所需的方法。当一个事件发生在他们的系统中(例如,文档变得更新)时,他们会发出一个Webhook,它沿着“用户1234更新了ID为1234的文档的文档” - 此信息完全无用本身,因此您需要通过对API的请求进行跟进,以将这些ID转换为您需要在Webhook的信息中采取行动的任何信息。但它也意味着如果第三方获得网络哈克的有效载荷,他们就无法做到任何事情。

其他更多的劳动密集型来处理验证就是实际上......处理验证。您需要从两种方式接近这一点:将自己验证为发件人,并认证您将数据发送到的消费者(端点)。

要验证您的WebHook消费者,您确实是您所说的是谁(以及您通过Webhook发送的数据是合法的),您可以使用秘密密钥签署Webhook有效载荷。它最容易对称这样做,但如果您愿意,您也可以使用公共/私人加密。条纹在请求标题中使用对称秘密密钥签署其Webhook有效载荷,并使用户在仪表板中访问该键,以便它们可以在其端点验证签名。

一旦您验证了您的消费端点,您将想考虑如何验证消费的端点本身,并且您在WebHook有效载荷中发送的敏感数据并不容易被黑客攻击。有两种方法可以解决这个问题:

这在主要的Webhook提供商(Dropbox,Stripe,Twilio等)中相当不常见,并且需要在您和您的消费者结束时进行一些额外的工作;但它确保安全性密切紧张。

这是处理有效载荷安全性最常见的方法:只通过HTTPS发送数据(即现在应该是显而易见的),并要求您的消费者提供他们使用的特定证书。例如,Twilio不会将Webhook数据发送到HTTPS端点,使用自签名证书。

你现在可能意识到,在开发人员体验和安全之间存在不可能的权衡。在Webhook中发送没有有用的信息可最大限度地减少安全风险,但需要对消费者进行更多的工作。包括Webhook有效载荷的信息,可以实现平滑的开发人员体验,但很难完全安全。这样,安全是开发人员体验的一部分,意味着您需要权衡风险并选择最适合您的应用程序。

您的Webhook系统不会是一个完美的消息队列,您不应该尝试使其成为一个 - 偶数公司,如条纹的保证几乎没有完整的订单,发送的事件数量以及您作为消费者所期望的其他人体工程学。普遍规则 - 以及您消费开发人员的期望 - 是您至少会发出一次活动,但这是关于它的。

当您将POST请求发送到数据库中的端点时,其中一些将不可避免地失败(DNS问题,不正确的路由等)。你想在某种程度上重试,但不是不断而不是永远。一般性最佳实践:

使用指数退避以慢慢增加重试之间的时间。为了避免大大的等待时间,通过截短的指数退避设置最大的退避时间(通常每天一次)(这是巧合,GCP如何处理其Pubsub主题)。

如果端点尚未响应一段时间,请将其标记为端点数据库中的“已损坏”并停止向其发送请求。

一旦您将端点标记为损坏,请向开发人员发送电子邮件,通知他们您无法到达端点,并且他们需要修复它。

理想情况下,您的所有消费端点都应该向所有帖子请求返回200多岁。如果他们不是,那就是你可以用来确定重试/失败的东西。

Webhook提供商通常不会保证事件将其符合终点。请参阅,例如,stripe:

Webhook提供商通常也不保证他们将通过Webhook发送多少事件,因此消费者需要在某种程度上宽容地估中其终点。请参阅,例如,Dropbox:

事件之间存在无序,并发和重复之间的事件之间存在重叠(如上可以在上面的屏幕截图中看到)。一般来说,您可以花时间在改进Webhook系统上尝试避免这些问题,但在野外看起来很罕见。

当您开始在构建Webhooks系统时,您可以提前设置一些事情,这将使事情更顺畅(超出您的花哨的Vim设置)。

在本地发送POST请求 - 尤其是您正在调试AUTH - 不起作用,因为公共互联网无法使用本地DEV服务器,因此它将无法从提供商服务接收Webhooks。您需要对公共URL进行测试 - 您可以将简单的服务器设置为测试端点,或者只使用NGROK等内容到隧道到您的localhost。

值得注意的是,前面建立一个你想通过Webhook发出的样本事件库。否则,您将通过像OKTA这样的外部提供商触发应用程序中的东西或更糟糕的是,如果您想在用户身份验证等时发送Webhook等),则需要触发应用程序。

如上所述,您需要某种数据库来存储您将Webhooks发送到的所有端点。架构应该看起来像:

除了一个简单的关系数据库之外,真的没有充分的理由以此为此开始,因为这张表不太可能会扩展到会给你有问题的任何东西。

每当Webhook被发送(以及有效载荷,时间发送等)时,它都会有助于调试(和遵守)目的。

这更像是一个高级架构音符,但它的业务系统发生在发生的情况("事件")以及您根据该事件所采取的行为之间应该有一层分离。 (就像发出Webhook)。如果您的Webhooks出现问题,您不希望将其影响您的其他应用程序。

条纹在发生事件时提供Webhooks(客户创建,卡收费等)。您可以通过UI(下面)或通过条带的Webhooks API(是的,用于配置条带Webhook的API)添加您的端点。

如果您是消费者,您可以在一个端点上接受所有这些WebHooks,或者设置多个端点(每个事件一个) - 在后一种情况下,API可能更有用。以下是添加新的Webhook端点的样本发布请求看起来像(来自Stripe的Docs):

就像我们上面覆盖的那样,条纹不保证对事件的排序,以及事件也可能出现在重复上。当WebHook发送出来时,他们的系统期望您的端点返回2xx - 如果没有,它们会以越来越稀疏的增量重试,直到他们最终将终端标记为破坏,并向您发送电子邮件。

我们已经概述了实现自己的Webhook发送系统的一些最佳实践,但随着您的增长,工作不会在此处停止。由于您的服务获得更多流行且越来越多的用户消耗了Webhooks,您可能需要找到扩展和提供越来越多的事件的方法,而无需额外延迟。

一个良好的开始是看看像Kafka(井,技术上是PUB / SUB系统)这样的基于事件的数据库,或者具有多个工人进程的AWS Kinesis进行实际的Webhook发送。如果您正在运行正在消耗Webhooks的系统的另一侧,则可以通过使用Web服务器前面的负载均衡器或反向代理来扩展Webhook摄取。如果您最终增长过去,您甚至可能想要调查基于非WebHook的事件流解决方案,如AMQP或TIBCO。

同时,查看Workos文档以查看我们如何实现常用有效负载和事件类型,以使您的应用程序就绪。