Google Cloud Tasks - 如何保护 HTTP POST 请求?

Google Cloud Tasks - How to secure HTTP POST request?

提问人:Muhammad Adnan 提问时间:6/15/2023 更新时间:6/15/2023 访问量:206

问:

我一直在我的一个应用程序中使用 Google Cloud Tasks,我使用 Google Cloud Tasks 进行事件调度,以便在事件状态发生变化后在 UI 中做出响应,如果达到特定的计划时间并调度任务。

而且,我发现HTTP POST请求存在问题,它并不完全安全。我在互联网上发现,任何只有互联网连接和我的有效负载模式数据的人都可以请求这个HTTP POST请求,并且可以在firestore中操作我的文档。

那是我的对象,我用来在队列中创建任务的触发器部分。taskonCreate

const task {
   name: `${taskName}/myTask-${snapshot.id}-${expireSeconds}`,
   httpRequest: {
     httpMethod: ‘POST’,
     url: `https://${location}-${project}.cloudfunctions.net/schedulerCallback?eventId=${snapshot.id}`,
     body: Buffer.from(JSON.stringify(payload)).toString(‘base64’),
     headers: { ‘Content-Type’: ‘application/json’ }
  }
   scheduleTime: {
     seconds: expirationAtSeconds
  }
}

要创建任务,请执行以下操作:

await tasksClient.createTask({ parent: queuePath, task });

这就是 HTTP 触发器,按计划调度时间到达。onRequest

export const schedulerCallback = functions.https.onRequest(async (req, res) => {
   // accessing the eventId that we sent over HTTP POST request as a query param.
   const eventId = req.query.eventId as string;
   try {
      await admin.firestore().collection(“events”).doc(eventId).delete();
   } catch(e) {
      console.log(e);
      res.status(500).send(e);
   }
});

我对此感到非常沮丧,并且没有找到任何解决方案来保护HTTP POST请求。我来到这里是堆栈溢出的,这里之前问过一些类似的问题,但超出了我的上下文。这与专门使用 Google Cloud Tasks 保护 HTTP POST 请求无关。

在付出了所有的努力和如此多的研究之后,我终于找到了解决方案。我很高兴与你分享,看看答案。

安全 google-cloud-platform 发布 https google-cloud-tasks

评论


答:

2赞 Muhammad Adnan 6/15/2023 #1

保护 HTTP 请求是一个可以解决的问题,因此我们无需担心。 您将如何做到这一点:

首先,在完成创建任务之前,然后处理它们。您需要一个具有特定角色的服务帐户。因此,使用该服务帐户对函数调用程序进行身份验证,并仅允许该服务帐户执行所有操作,例如调用 Cloud Functions 触发器、创建服务帐户令牌,最后将任务排入队列。

考虑到这一点,请转到 IAM & Admin > Service Accounts(服务帐户)创建服务帐户。在顶部菜单上,您将看到 CREATE SERVICE ACCOUNT。为您的服务帐户命名,然后转到 CREATE AND CONTINUE。在第二步中,为服务帐号(Cloud Function Invoker、Cloud Tasks Enqueuer 和 Service Account Token Creator)授予以下角色,即可完成服务帐号的使用。

接下来,在“IAM和管理”上,进入第一个名为IAM的选项卡。单击“授予访问权限”,然后在第一个字段“新委托人”中添加您的服务帐户,并授予其上述角色并保存它。

现在,在触发器中添加一个额外的字段,如下所示:onCreate

const serviceAccountEmail = 'YOUR_SERVICE_ACCOUNT_HERE';

接下来,您的对象配置将如下所示:task

const task {
    name: `${taskName}/myTask-${snapshot.id}-${expireSeconds}`,
      httpRequest: {
    httpMethod: ‘POST’,
    url: `https://${location}-${project}.cloudfunctions.net/schedulerCallback?eventId=${snapshot.id}`,
    body: Buffer.from(JSON.stringify(payload)).toString(‘base64’),
       headers: { ‘Content-Type’: ‘application/json’ }
    }
   oidcToken: {
     serviceAccountEmail,
     audience: “https://${location}-${project}.cloudfunctions.net/schedulerCallback”
   }
     scheduleTime: {
     seconds: expirationAtSeconds
  }
}

在您设置了服务帐户并使用 配置了任务后,每次触发函数时,Google Cloud Tasks 都会收到有关谁应发出 HTTP 请求以及应将请求发送到何处的说明。oidcTokenonCreate

该字段的指定与 url 相同(不包括查询参数)。这样做的主要目的是验证令牌是否用于目标服务(在本例中为我们的云函数)。audiencehttpRequest

当计划时间到达且任务即将分派时,服务帐户指定字段用于生成 OpenID Connect (OIDC) 令牌。此令牌声明了服务帐号的身份,包括与您的 Cloud Function 网址匹配的声明。serviceAccountEmailaudience

当任务完全分派时,OIDC 令牌将作为 HTTP 请求的 Authorization 标头的一部分发送。当您的 Cloud Function 收到请求时,它可以验证令牌,以确保请求已通过身份验证和授权。它验证声明是否与其自己的 URL 匹配,确认令牌用于此服务。audience

接下来,创建一个异步函数来验证令牌:

async function verifyToken(token: string, location: any,project: any) {
    const client = new OAuth2Client();
    const ticket = await client.verifyIdToken({
        idToken: token,
        audience: `https://${location}-${project}.cloudfunctions.net/updateEventStatus`,
    });

    return ticket.getPayload();
}

上面的函数使用,所以你需要安装和导入。OAuth2Clientgoogle-auth-library

// install library
$ npm install google-auth-library
// import library
import { OAuth2Client } from “google-auth-library”; 

现在你的将看起来像这样:schedulerCallback

export const schedulerCallback = functions.https.onRequest(async (req, res) => {
   // add the projectID and location
   const project = json.parse(process.env.FIREBASE_CONFIG!).projectId;
   const location = ‘us-central1’;
   const eventId = req.query.eventId as string;

   // access the headers > authorization of the request
    const authorizationHeader = req.headers.authorization;

  // check if authorization header is not null
   if(!authorizationHeader) {
      res.status(401).send(“unauthorized token”);
      return;
   }

   // if authorizationHeader is not null access the token
    const token = authorizationHeader.split(‘ ’)[1];

   // verify ID token
    try {
      await verifyIdToken(token, location, project);
    } catch (error) {
      console.log(error);
      res.status(401).send(“Unauthorized token”);
      return;
    }

   // delete document
   try {
      await admin.firestore().collection(“events”).doc(eventId).delete();
   } catch(e) {
      console.log(e);
      res.status(500).send(e);
   }
});

完成此操作后,现在您的 HTTP POST 请求是安全的,并且除了 Internet 连接之外,没有人可以操作 firestore 中的文档。但只有你的应用才有权这样做。

要详细了解 Google Cloud 任务调度,请按照我的 Medium 博客文章中的分步指南进行操作。

享受!