【问题标题】:Google App Engine: secure inter-app communicationGoogle App Engine:安全的应用间通信
【发布时间】:2016-03-31 01:28:52
【问题描述】:

我在 Google App Engine 上有两个应用程序,它们都在同一个帐户下运行,一个通过 HTTPS 调用另一个提供的服务。确保只允许第一个应用程序调用第二个应用程序的推荐方法是什么?

或者,有没有办法指定给定端点只能由在同一 GAE 帐户下运行的应用调用?

【问题讨论】:

    标签: security google-app-engine http


    【解决方案1】:

    让您的应用检查“X-Appengine-Inbound-Appid”标头并确保应用 ID 正确。仅当请求由另一个 Google App Engine 应用发出且用户无法修改时,此标头才存在。

    如果您使用的是 Python,您可以执行以下操作:

    import webapp2
    
    AUTHORIZED_APPS = ('my-first-app', 'my-other-app')
    
    class MyHandler(webapp2.RequestHandler):
        def dispatch(self):
            app_id = self.request.headers.get('X-Appengine-Inbound-Appid', None)
    
            if app_id in AUTHORIZED_APPS:   
                super(MyHandler, self).dispatch()
            else:
                self.abort(403)
    

    对于标头中没有 X-Appengine-Inbound-Appid 的任何请求,这将引发 403。

    此外,当使用 urlfetch 从一个应用程序向另一个应用程序发出请求时,请确保设置 follow_redirects=False 或不添加标头。

    【讨论】:

    • 知道授权应用的应用ID的人不能通过手动添加标题来模拟它们吗?
    • 没有。 “此标头不能被其他应用或外部服务伪造 - 它已被 App Engine 系统清理”stackoverflow.com/a/7961397/789417
    • 如果 X-Appengine-Inbound-Appid 标头不存在,上述代码将无法实际工作。如果标头不存在,app_id 将为无。检查 None 是否在元组中将产生 TypeError。您真正想要的是 AUTHORIZED_APPS 成为一个列表。检查 None 是否在列表中是 Python 中的一项有效操作。
    • @timbo 那是错误的。如果你得到一个 TypeError,那么你不是在检查一个元组。您很可能正在检查一个字符串。如果您仅使用一项声明元组,例如 APPS = ("my-app"),那么您不是在声明一个元组,而是一个字符串。您需要使用 APPS = ("my-app", )。
    【解决方案2】:

    正如其他人所指出的,依靠填写 X-Appengine-Inbound-Appid 标头是最简单的解决方案。我最近遇到了类似的问题,但我无法使用 X-Appengine-Inbound-Appid,因为 URLFetch 不可用(GAE Node.js)。以下是使用通过 OAuth 进行身份验证的服务帐户解决问题的方法。

    在发件人方面,您需要设置一个服务帐户: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount

    然后在该应用程序中,您将需要获取服务帐户凭据: https://developers.google.com/identity/protocols/application-default-credentials

    然后您可以使用凭据来创建 authClient,您可以使用它来发送请求。您需要将 OAuth 范围添加到您的 authClient。最合乎逻辑的使用是https://www.googleapis.com/auth/userinfo.email。这将使接收者能够获取发件人服务帐户的电子邮件地址。 https://developers.google.com/identity/protocols/googlescopes

    这是使它在(发送者)Node.js 中工作的代码:

    process.env.GOOGLE_APPLICATION_CREDENTIALS = <PATH TO CREDENTIALS FILE>
    
    google.auth.getApplicationDefault((err, authClient) => {
      if (err) {
        console.log("Failed to get default credentials: ", String(err));
        return;
      }
    
      if (authClient.createScopedRequired && authClient.createScopedRequired()) {
        authClient = authClient.createScoped([
          'https://www.googleapis.com/auth/userinfo.email'
        ]);
      }
    
      auth_client.request({
        url: <RECEIVER URL>,
        method: "GET"
      }, (error, result, response) => {
        // Process response
      });
    });
    

    然后在接收方,您需要验证电子邮件地址是否与发件人服务帐户的电子邮件地址匹配。当应用引擎在本地调用时,OAuth 请求未正确验证,因此如果您想在本地进行测试,则必须自己进行 url 获取来验证请求。

    接收器 Python:

    scope = "https://www.googleapis.com/auth/userinfo.email"
    allowed_users = set([
        "<SENDER SERVICE ACCOUNT EMAIL>"
    ])
    IS_DEV = os.environ["SERVER_SOFTWARE"][:3] == "Dev"
    
    class MyHandler(webapp2.RequestHandler):
        def get(self, clientId):
            user = self.get_current_user()
            if user in allowed_users:
                # Do whatever you wanted
    
        def get_current_user(self):
            if IS_DEV:
                token = self.request.headers.get("Authorization")[len("Bearer "):]
                response = urlfetch.fetch(
                    "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=%s" % token
                )
                return json.loads(response.content)["email"]
            else:
                return oauth.get_current_user(scope)
    

    【讨论】:

    • 感谢 sn-ps :)
    【解决方案3】:

    您不妨查看 App Engine 的 Application Identity 服务。

    【讨论】:

    • 谢谢;这可能比我需要的更复杂,但很高兴知道。
    【解决方案4】:

    最简单的方法(在这种情况下已经足够好了)是在 HTTP 标头中包含一个秘密并在服务端检查它。

    【讨论】:

    • 谢谢,这就是我目前正在做的事情。我想知道是否有一些特定于 App Engine 的方法可以利用应用在同一帐户下运行这一事实来处理此问题。
    【解决方案5】:

    不知道这是否可行,但您可以尝试在 app.yaml 中将“登录:管理员”添加到您的 url 处理程序。

    它适用于 cron/任务队列作业

    https://developers.google.com/appengine/docs/python/config/cron#Securing_URLs_for_Cron

    【讨论】:

      猜你喜欢
      • 2019-01-17
      • 2016-03-25
      • 2011-02-01
      • 2021-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多