使用身份验证保护Google Cloud Functions的HTTP触发器

49

我今天按照这个指南尝试使用Google云函数:https://cloud.google.com/functions/docs/quickstart

我创建了一个具有HTTP触发器的函数,并能够执行POST请求以触发函数写入Datastore。

我想知道是否有一种方法可以保护此HTTP端点?目前似乎它将接受来自任何地方/任何人的请求。

在谷歌搜索时,我看到大多数结果都是关于使用Firebase来保护内容的。但是,在这里我没有使用Firebase服务。

我的选择是让其保持开放状态,并希望没有人知道URL端点(安全性通过模糊性),还是在函数本身中实现自己的身份验证检查?


2
我有同样的问题! - Derek Cheng
2
我遇到了同样的问题! - Neurus
11
不是重复的。 - Tri Nguyen
大家好,我们在Alpha版中推出了一项功能,允许您基于每个函数设置IAM策略以限制调用。请在bit.ly/gcf-iam-alpha注册,我会为您提供进入权限 :) - Mike McDonald
谢谢@MikeMcDonald。我已经注册了。 - Tri Nguyen
显示剩余6条评论
7个回答

15

进一步研究后,参考@ricka的回答,我决定在我的云函数中实现身份验证检查,并通过Authorization头访问令牌以JWT令牌的形式传递。

以下是Node的实现:

const client = jwksClient({
  cache: true,
  rateLimit: true,
  jwksRequestsPerMinute: 5,
  jwksUri: "https://<auth0-account>.auth0.com/.well-known/jwks.json"
});

function verifyToken(token, cb) {
  let decodedToken;
  try {
    decodedToken = jwt.decode(token, {complete: true});
  } catch (e) {
    console.error(e);
    cb(e);
    return;
  }
  client.getSigningKey(decodedToken.header.kid, function (err, key) {
    if (err) {
      console.error(err);
      cb(err);
      return;
    }
    const signingKey = key.publicKey || key.rsaPublicKey;
    jwt.verify(token, signingKey, function (err, decoded) {
      if (err) {
        console.error(err);
        cb(err);
        return
      }
      console.log(decoded);
      cb(null, decoded);
    });
  });
}

function checkAuth (fn) {
  return function (req, res) {
    if (!req.headers || !req.headers.authorization) {
      res.status(401).send('No authorization token found.');
      return;
    }
    const parts = req.headers.authorization.split(' ');
    if (parts.length != 2) {
      res.status(401).send('Bad credential format.');
      return;
    }
    const scheme = parts[0];
    const credentials = parts[1];

    if (!/^Bearer$/i.test(scheme)) {
      res.status(401).send('Bad credential format.');
      return;
    }
    verifyToken(credentials, function (err) {
      if (err) {
        res.status(401).send('Invalid token');
        return;
      }
      fn(req, res);
    });
  };
}

我使用jsonwebtoken来验证JWT令牌,使用jwks-rsa来检索公钥。我使用Auth0,因此jwks-rsa会访问公钥列表以检索它们。

然后可以使用checkAuth函数来保护云函数,如下所示:

exports.get = checkAuth(function (req, res) {
    // do things safely here
});

你可以在我的github库中看到这个变化,网址是https://github.com/tnguyen14/functions-datastore/commit/a6b32704f0b0a50cd719df8c1239f993ef74dab6
JWT / 访问令牌可以通过多种方式获取。对于Auth0,API文档可以在https://auth0.com/docs/api/authentication#authorize-client找到。
一旦完成了这些步骤,您就可以触发云函数(如果已启用http触发器),例如:
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer access-token" \
-d '{"foo": "bar"}' \
"https://<cloud-function-endpoint>.cloudfunctions.net/get"

这是目前最好的解决方案吗? - sdfsdf
1
这是否意味着我们必须将这段代码放入我们编写的所有云函数中? - Mahadevan Sreenivasan
是的,我把它放在一个模块中,并从所有函数中引用它。 - Tri Nguyen

14

三年后,我仍在为同样的问题纠结了一整天,而谷歌的文档并没有很好地解释。对于那些不想使用代码实现(像我一样)的人,我在下面概述如何仅使用GCP控制台对Cloud Functions进行身份验证。以下是一个示例,它将HTTP触发器验证到一个新的服务帐户,然后安排在Cloud Scheduler中运行。您可以进一步扩展和通用化以满足其他需求。

假设: 1.您已经创建了一个使用HTTP的Cloud Function,并使其需要身份验证。 2.在执行测试运行时,您的功能可以正常工作。这很重要,您不希望以后一次解决两个或更多的问题。 3.您知道如何使用GCP网页浏览器控制台。

步骤:

  1. 我建议创建一个新的服务帐户,用于调用HTTP Cloud Function的任务。通过GCP的“IAM和管理”页面完成此操作。转到“服务帐户”,然后单击“创建新帐户” 输入图像描述信息

  2. 命名您的新服务帐户。基于您的命名,将自动生成一个服务帐户ID。它看起来像是GCP服务帐户电子邮件。“@yourproject-name.iam.gserviceaccount.com”。复制此内容以备后用。单击“创建”按钮完成新帐户的创建。

  • 在下一页中,您需要为服务账户选择一个角色。最佳实践是仅运行函数的“Cloud Functions Invoker”。单击“继续”按钮。您可以跳过第三部分(授予用户访问该服务账户的权限)。 enter image description here

  • 现在,将此新服务账户添加到需要受保护的云函数中。进入Cloud Function面板,并勾选函数名称左侧的框。然后在同一面板的右上方,单击“显示信息面板” - 注意屏幕上需要进行身份验证。(必须从此处添加,而不是从函数“权限”页面添加,您无法从那里添加新成员。) enter image description here

  • 现在将服务账户添加为新成员。将您之前复制的服务账户电子邮件粘贴到红框中的空白字段中。您必须输入电子邮件帐户,仅输入名称将无法正常工作。对于“角色” - 在下拉菜单中再次选择“Cloud Functions Invoker”。点击“保存”。 enter image description here

  • 在 Cloud Function 的属性中,有提供的 HTTP 触发器,请复制您的触发器并将其保留以备后用。 enter image description here

  • 现在进入 Google Cloud Scheduler 并选择一个计划任务(如果你还没有计划任务,可以创建一个。下面的屏幕截图显示已经创建了一个计划任务)。 enter image description here

  • 选中计划任务框后,点击“编辑”按钮,将会出现下面的屏幕截图。在最初的界面底部选择“显示更多”以查看所有字段。与权限相关的重要字段如下:

  • 对于“URL”- 粘贴步骤 6 中复制的触发器 URL。
    对于“Auth Header”选择 OIDC token。这些由 GCP 管理,足以进行身份验证。
    对于“Service Account”,粘贴上面步骤中使用的相同账号。
    “Audience”将自动填充,无需输入任何内容。 完成后,根据您的入口点,单击“更新”或“保存”按钮。 enter image description here

    1. 返回到 Cloud Scheduler 仪表板,通过点击“立即运行”按钮来运行你的函数。如果一切顺利,它应该会运行并显示“成功”的状态。如果没有,请查看日志以了解发生了什么。 enter image description here

    2. 现在,你知道已为你的认证 Cloud Function 创建了服务帐户。从这里开始,你可以按照你的项目需求,在此服务帐户的上下文中执行各种操作。

    3. 作为检查,请确保将 HTTP 触发器 URL 粘贴到浏览器中以确保它无法运行。你应该会收到以下“禁止访问”: enter image description here


    太棒了,谢谢!不过我希望官方的谷歌文档也能这么好理解。 - Alex Shevchyshen
    5
    谢谢您的优秀回答。现在我想从第三方应用程序(如LMS或任何自定义服务器)调用该HTTP函数,那么我该如何确保HTTP函数由您在此处创建的服务帐户执行?在此处,您已将服务帐户添加到计划程序中,但如果我从第三方服务器调用它怎么办? - Manish
    感谢您提供如此详细的答案。我很好奇,第三步和第五步不是一样的吗? 我认为第五步只是从云函数界面快速添加服务账户,而不必进入IAM选项卡等方式。 - Urvah Shabbir
    1
    我很好奇你是否使用了相同的服务账户(你创建的那个)作为“运行时服务账户”? - Urvah Shabbir
    我也在寻找像Manish上面评论的解决方案,如何让任何经过身份验证的用户可以通过CURL、Postman等方式触发该功能? - kwick

    4

    您可以在函数之外设置项目范围或每个函数的权限,这样只有经过身份验证的用户才能触发函数,即使他们尝试访问终端节点。

    以下是Google Cloud Platform关于设置权限用户身份验证的文档。请注意,截至撰写本文时,我认为使用此方法需要用户使用Google账号进行身份验证。


    是的,这是最近才有的东西。在我发帖时还没有。 - Tri Nguyen
    2
    确实 - 只是为了让那些在2020年来访的人保持最新的选择。 - ultraGentle

    1

    0

    0

    就目前而言,看起来已经进行了一些升级,Google Cloud Functions现在支持两种身份验证和授权类型:Identity and Access Management(IAM)和OAuth 2.0。文档可以在这里找到。


    0

    目前似乎有两种方法来保护 Google Cloud Function HTTP 端点。

    1)使用难以猜测的函数名称(例如:my-function-vrf55m6f5Dvkrerytf35)

    2)在函数本身中检查密码/凭据/签名请求(使用标头或参数)

    最好同时采用这两种方法。


    是的,看起来认证是需要在函数内部进行检查的。 - Tri Nguyen
    11
    “使用难以猜测的函数名称”不是一个解决方案。如果有人监听连接会怎样? - Berkay Turancı

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接