验证来自 Google Cloud Scheduler 的 HTTP 请求

Posted

技术标签:

【中文标题】验证来自 Google Cloud Scheduler 的 HTTP 请求【英文标题】:Verify HTTP request from Google Cloud Scheduler 【发布时间】:2019-04-10 09:37:03 【问题描述】:

验证来自 Google Cloud 调度程序的 HTTP 请求的流程是什么?文档 (https://cloud.google.com/scheduler/docs/creating) 提到您可以创建一个以任何公开可用的 HTTP 端点为目标的作业,但没有提到服务器如何验证 cron/scheduler 请求。

【问题讨论】:

【参考方案1】:

[2019 年 5 月 28 日更新]

Google Cloud Scheduler 现在有两个命令行选项:

--oidc-service-account-email=<service_account_email>
--oidc-token-audience=<service_endpoint_being_called>

这些选项会为 Cloud Scheduler 发出的请求添加一个额外的标头:

 Authorization: Bearer ID_TOKEN

您可以在端点代码中处理 ID_TOKEN 以验证谁在调用您的端点。

例如,您可以发出 HTTP 请求来解码 ID Token:

https://oauth2.googleapis.com/tokeninfo?id_token=ID_TOKEN

这将像这样返回 JSON:


  "aud": "https://cloudtask-abcdefabcdef-uc.a.run.app",
  "azp": "0123456789077420983142",
  "email": "cloudtask@development.iam.gserviceaccount.com",
  "email_verified": "true",
  "exp": "1559029789",
  "iat": "1559026189",
  "iss": "https://accounts.google.com",
  "sub": "012345678901234567892",
  "alg": "RS256",
  "kid": "0123456789012345678901234567890123456789c3",
  "typ": "JWT"

然后,您可以检查服务帐户电子邮件是否与您授权 Cloud Scheduler 使用的电子邮件相匹配,并且该令牌尚未过期。

[结束更新]

您需要自己验证请求。

Google Cloud Scheduler 包含几个 Google 特定的标头,例如 User-Agent: Google-Cloud-Scheduler。请参阅下面的文档链接。

但是,任何人都可以伪造 HTTP 标头。您需要创建一个自定义 something,将其作为 HTTP 标头或包含在您知道如何验证的 HTTP 正文中。使用签名的 JWT 将是安全且易于创建和验证的。

当您创建 Google Cloud Scheduler 作业时,您可以对 headersbody 字段进行一些控制。您可以将自定义 something 嵌入其中之一。

Scheduler Jobs

[更新]

这是一个使用 gcloud 的示例(Windows 命令行),以便您可以设置 HTTP 标头和正文。此示例在每个触发器上调用 Cloud Functions,展示如何包含 APIKEY。 Google 控制台还没有这种级别的支持。

gcloud beta scheduler ^
--project production ^
jobs create http myfunction ^
--time-zone "America/Los_Angeles" ^
--schedule="0 0 * * 0" ^
--uri="https://us-central1-production.cloudfunctions.net/myfunction" ^
--description="Job Description" ^
--headers=" \"Authorization\": \"APIKEY=AUTHKEY\", \"Content-Type\": \"application/json\" " ^
--http-method="POST" ^
--message-body="\"to\":\"/topics/allDevices\",\"priority\":\"low\",\"data\":\"success\":\"ok\""

【讨论】:

谢谢!请注意,目前 Google Cloud Platform 控制台 UI 仅允许您设置自定义 POST/PUT 正文。 UI 中不提供设置自定义 HTTP 标头的功能。 @jrmerz - 我刚刚更新了我的答案以显示一个 gcloud 示例设置 HTTP 标头和正文。我希望 Google 控制台在测试版发布时能够支持此功能。 这句话However, anyone can forge HTTP headers是不正确的。不要指望这一点,但我在某处读到,谷歌删除了所有带有禁止前缀的标题,例如(我可能弄错了)X-Google,等等 @Prometheus - 我每天都用 curl 和 Python 脚本伪造标题,包括对 Cloud Functions 的请求。我们针对面向公众的软件和 API 的漏洞测试的一部分。如果您有评论的参考,请添加它。 @johnhanley 当然,groups.google.com/forum/#!topic/google-appengine/FAxqswxW4dk 这是略有不同的问题,但要点是相同的 - 验证请求的来源【参考方案2】:

简答

如果您将应用托管在 Google Cloud 中,只需检查标头 X-Appengine-Queuename 是否等于 __scheduler。但是,这是未记录的行为,有关更多信息,请阅读下文。

此外,如果可能,请使用 Pub/Sub 而不是 HTTP 请求,因为 Pub/Sub 是内部发送的(因此具有隐式验证的来源)。


实验

正如我发现的 here,Google 会去除某些标头的请求1,但不是全部2。让我们看看 Cloud Scheduler 是否有这样的 headers。

1 例如您不能发送任何 X-Google-* 标头(通过实验发现,read more)

2 例如您可以发送X-Appengine-* 标头(通过实验找到)

实验中使用的Flask应用:

@app.route('/echo_headers')
def echo_headers():
    headers = h[0]: h[1] for h in request.headers
    print(headers)
    return jsonify(headers)

Cloud Scheduler 发送的请求标头


  "Host": []
  "X-Forwarded-For": "0.1.0.2, 169.254.1.1",
  "X-Forwarded-Proto": "http",
  "User-Agent": "AppEngine-Google; (+http://code.google.com/appengine)",
  "X-Appengine-Queuename": "__scheduler",
  "X-Appengine-Taskname": [private]
  "X-Appengine-Taskretrycount": "1",
  "X-Appengine-Taskexecutioncount": "0",
  "X-Appengine-Tasketa": [private]
  "X-Appengine-Taskpreviousresponse": "0",
  "X-Appengine-Taskretryreason": "",
  "X-Appengine-Country": "ZZ",
  "X-Cloud-Trace-Context": [private]
  "X-Appengine-Https": "off",
  "X-Appengine-User-Ip": [private]
  "X-Appengine-Api-Ticket": [private]
  "X-Appengine-Request-Log-Id": [private]
  "X-Appengine-Default-Version-Hostname": [private]

证明标头 X-Appengine-Queuename 已被 GAE 剥离

限制

Google SLA 和折旧政策很可能不支持此方法,因为它没有记录在案。另外,我不确定当请求源在 Google Cloud 中时是否无法伪造标头(也许它们在外层被剥离)。我已经在 GAE 中使用应用程序进行了测试,结果可能会或可能不会因其他部署选项而异。简而言之,使用风险自负。

【讨论】:

【参考方案3】:

这个标题应该可以工作:

映射(键:字符串,值:字符串)

HTTP 请求标头。

此映射包含标题字段名称和值。标头可以 在创建作业时设置。

Cloud Scheduler 将一些标头设置为默认值:

User-Agent:默认情况下,此标头为“AppEngine-Google; (+http://code.google.com/appengine)"。这个标头可以修改,但是 Cloud Scheduler 将附加“AppEngine-Google; (+http://code.google.com/appengine)" 到修改后的用户代理。 X-CloudScheduler:此标头将设置为 true。 X-CloudScheduler-JobName:此标头将包含作业名称。 X-CloudScheduler-ScheduleTime:用于指定的 Cloud Scheduler 作业 unix-cron 格式,此标头将包含作业计划时间 采用 RFC3339 UTC“祖鲁”格式。如果作业有正文,则 Cloud Scheduler 设置以下标题:

Content-Type:默认情况下,Content-Type 标头设置为 “应用程序/八位字节流”。默认值可以被显式覆盖 作业时将 Content-Type 设置为特定的媒体类型 创建的。例如,Content-Type 可以设置为“application/json”。 Content-Length:这是由 Cloud Scheduler 计算的。这个值是 仅输出。它不能改变。以下标题仅用于输出。 它们不能被设置或覆盖:

X-Google-:仅供 Google 内部使用。 X-AppEngine-:对于谷歌 仅供内部使用。此外,一些 App Engine 标头,其中包含 作业特定的信息,也会被发送到作业处理程序。

包含“键”列表的对象:值对。示例:“名称”: “扳手”、“质量”:“1.3kg”、“计数”:“3”。

https://cloud.google.com/scheduler/docs/reference/rest/v1/projects.locations.jobs#appenginehttptarget

【讨论】:

【参考方案4】:
if request.META['HTTP_X_CLOUDSCHEDULER'] == 'true':
   print("True")

【讨论】:

以上是关于验证来自 Google Cloud Scheduler 的 HTTP 请求的主要内容,如果未能解决你的问题,请参考以下文章

Google Cloud Tasks 无法向 Cloud Run 进行身份验证

获取来自 Google Cloud Messaging 的 json 数据

Google Cloud Endpoints 和用户身份验证

来自 DataProc 集群的 Google Cloud Sdk

Google Cloud Run 最终用户身份验证

Google Cloud Endpoints:身份验证问题(错误 403)