使用 AWS API Gateway 的 Github Webhooks 秘密

Posted

技术标签:

【中文标题】使用 AWS API Gateway 的 Github Webhooks 秘密【英文标题】:Github Webhooks secret with AWS API Gateway 【发布时间】:2020-01-26 05:03:09 【问题描述】:

我正在尝试让 Github Webhook 启动我拥有的 AWS Lambda。 我能弄清楚如何做到这一点的最好方法是使用 AWS API Gateway,问题是安全性。

Github Webhooks 只会通过 POST 调用发送秘密。

我找不到任何方法让 AWS API Gateway 验证此签名。 或者我可以在哪里添加此功能。

我假设我可以写一个AWS Lambda Authorizer。 但是这是很多不同地方的代码,开始看到需要serverless 框架。

AWS 中是否有任何更简单的设置我不知道?

【问题讨论】:

【参考方案1】:

来到这里是因为我试图将 Github webhook 与 AWS lambda 集成并遇到与 OP 相同的问题。在撰写本文时,我认为最好的解决方案是在主 lambda 中包含验证码,正如其他人所建议的那样。

在 2017 年 9 月的 AWS 计算机博客上:

增强型请求授权器 Lambda 函数接收类似于代理集成的事件对象。它包含有关请求的所有信息,不包括正文。

来源: Using Enhanced Request Authorizers in Amazon API Gateway (amazon.com)

您无法按照 Github 的建议执行 HMAC,因为 AWS 授权器 lambda 不会让您访问 HTTP 请求的主体,而您需要它来比较摘要。

这是一种耻辱,因为 HMAC 似乎是保护响应 webhook 的端点的一种非常标准的方法。例如,参见这篇博文,Webhooks do’s and dont’s: what we learned after integrating +100 APIs (restful.io)。 Twitter 和 Stripe 做了类似的事情:

Securing webhooks (twitter.com) Checking Webhook Signatures(stripe.com)

要使上述方法有效,如果您使用的是 API Gateway,您需要确保将包含哈希签名的标头作为 event 参数的一部分转发给 lambda。为此,请按照以下说明操作:How do I pass custom headers through Amazon API Gateway to an AWS Lambda function using custom Lambda integration for further processing? (amazon.com)

【讨论】:

实际上,我只是在一次会议上与 AWS Authorizer 产品经理交谈,他很失望地得知他自己无法做到这一点,因为您在这里所说的正是如此(无法访问有效负载反对),他的回应是在 Lambda 中进行。有点遗憾,您不能将他们的系统用于他们的最佳实践。这是一个惊人的回应,你赢得了这个答案!谢谢,希望我们能帮助许多其他人,直到 AWS 想出更好的方法来处理这个问题。【参考方案2】:

我找不到使用 API Gateway 执行此操作的方法。我使用(Python)在 LAMBDA 中进行了验证。

高级概述:使用 GITHUB_SECRET 计算 HMAC 签名,然后与从 Github 传递的签名进行比较。

您显然可以简化,故意冗长以提高可读性。可能有更好的方法,但我找不到。

确保您的 Webhook 已针对 application/json 进行了配置。希望这对其他人有帮助。

import logging
import json
import hmac
import hashlib
import re
from urllib.parse import unquote

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

GITHUB_SECRET = 'SECRET FROM GITHUB CONSOLE'


def lambda_handler(event, context):
    logger.info("Lambda execution starting up...")

    incoming_signature = re.sub(r'^sha1=', '', event['headers']['X-Hub-Signature'])
    incoming_payload = unquote(re.sub(r'^payload=', '', event['body']))
    calculated_signature = calculate_signature(GITHUB_SECRET, incoming_payload.encode('utf-8'))

    if incoming_signature != calculated_signature:
        logger.error('Unauthorized attempt')
        return 
            'statusCode': 403,
            'body': json.dumps('Forbidden')
        

    logger.info('Request successfully authorized')

    # do stuff in Lambda

    return 
        'statusCode': 200,
        'body': json.dumps(f'Work in progress')
    


def calculate_signature(github_signature, githhub_payload):
    signature_bytes = bytes(github_signature, 'utf-8')
    digest = hmac.new(key=signature_bytes, msg=githhub_payload, digestmod=hashlib.sha1)
    signature = digest.hexdigest()
    return signature

【讨论】:

感谢分享!是的,我实际上有您编写的用于在单独的 lambda 中进行验证的内容,但我需要它来返回策略和委托人 ID,我对应该发送什么 AWS 策略感到非常困惑?允许 API 网关的策略,或者允许我想要后台功能的实际 lambda。 这就是我接下来要做的事情。以上假设您将内联完成授权,然后运行 ​​lambda 代码(都在同一个 lambda 中)。当我有时间让 lambda 返回 IAM 策略和委托人标识符时,我会分享。 在此处出现错误 :( [ERROR] TypeError: expected string or bytes-like object Traceback (last recent call last): File "/var/task/lambda_function.py", line 21, in lambda_handler incoming_payload = unquote(re.sub(r'^payload=', '', event['body-json'])) 文件“/var/lang/lib/python3.8/re.py”,第 208 行, in sub return _compile(pattern, flags).sub(repl, string, count)

以上是关于使用 AWS API Gateway 的 Github Webhooks 秘密的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 AWS CloudFormation 在 AWS API Gateway 上应用安全策略?

使用 AWS API Gateway 进行 API 版本控制

AWS API Gateway:如何实现持续交付?

使用 AWS API Gateway 验证会话

使用授权方后 AWS Amplify API Gateway cors 错误:aws_iam

如何使用 CloudFormation 模板更新现有 AWS API Gateway