提问人:Muhammad Adnan 提问时间:6/15/2023 更新时间:6/15/2023 访问量:206
Google Cloud Tasks - 如何保护 HTTP POST 请求?
Google Cloud Tasks - How to secure HTTP POST request?
问:
我一直在我的一个应用程序中使用 Google Cloud Tasks,我使用 Google Cloud Tasks 进行事件调度,以便在事件状态发生变化后在 UI 中做出响应,如果达到特定的计划时间并调度任务。
而且,我发现HTTP POST请求存在问题,它并不完全安全。我在互联网上发现,任何只有互联网连接和我的有效负载模式数据的人都可以请求这个HTTP POST请求,并且可以在firestore中操作我的文档。
那是我的对象,我用来在队列中创建任务的触发器部分。task
onCreate
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 请求无关。
在付出了所有的努力和如此多的研究之后,我终于找到了解决方案。我很高兴与你分享,看看答案。
答:
保护 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 请求以及应将请求发送到何处的说明。oidcToken
onCreate
该字段的指定与 url 相同(不包括查询参数)。这样做的主要目的是验证令牌是否用于目标服务(在本例中为我们的云函数)。audience
httpRequest
当计划时间到达且任务即将分派时,服务帐户指定字段用于生成 OpenID Connect (OIDC) 令牌。此令牌声明了服务帐号的身份,包括与您的 Cloud Function 网址匹配的声明。serviceAccountEmail
audience
当任务完全分派时,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();
}
上面的函数使用,所以你需要安装和导入。OAuth2Client
google-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 博客文章中的分步指南进行操作。
享受!
评论