在 AWS Lambda 中使用 JavaScript eval()

Using JavaScript eval() in AWS Lambda

我想使用 AWS Lambda 来 运行 JavaScript 用户通过网络提交的代码。我的 Lambda 函数将 return 返回 return 值、stderrstdout。这样做会 运行 遇到什么问题?

恶意用户是否能够提交导致 Lambda 函数出现问题的代码?用户所做的更改(例如对节点环境或文件系统的更改)是否会在调用之间持续存在?有什么办法可以避免吗?

我可以将文件写入 Lambda 文件系统并调用:

而不是 eval()
const userCodeProcess = require('child_process').fork('user_code.js')
userCodeProcess.on('message', response.send)

用户将能够向您的 Lambda 函数提交代码,这在某些情况下可能会导致问题:

  • 文件系统 - 您的用户可以提交可以对您的文件系统进行更改的代码,并且这些更改将在 重用容器 [=54 上调用的函数中持续存在=].并非所有用户代码请求都会 运行 在同一个容器上,但是如果两个请求靠近同一个容器并且同一个 "scratch" 磁盘 space 将是可访问的。一种可能防止这种情况的方法是,如果您可以将包含的函数(例如 fs)限制到您在函数调用时定义的特定目录,这样您就可以为 [=11= 中的每个请求创建一个随机目录] 目录。我不确定这是否可能。您必须确保用户不能自己再次要求 fs。更好的建议是使用像 safe-eval 这样的东西,我稍后会详细讨论。
  • 环境变量 - eval中的代码运行可以访问Node环境变量。如果您需要向数据库、其他 AWS 产品等发出任何请求,那么您需要为 Lambda 函数提供一个策略,除非您对凭证进行硬编码,并且用户代码可以访问该信息。您必须确保您提供给系统的任何凭据不在环境变量中,如果是,那么您可以让用户访问它们。
  • 节流 - 您可能 运行 遇到节流问题。默认情况下,AWS 只允许 100 个并发调用。如果您不限制用户提交自己的 JS 的请求,那么您可能 运行 遇到由于达到限制而导致请求被拒绝的问题。我最近请求增加限制,并且我能够将我的并发请求增加到 3000 个,而无需他们评估他们当前的基础设施是否可以处理它,所以他们也可以给你 no-problem。
  • 费用 - 我确定您已经考虑过函数超时问题,但您希望确保函数的上限 运行 时间,以便您的用户不会增加您的 AWS 账单。此外,它可能看起来很小,但在大规模情况下,一堆 6MB 的请求响应加起来,因此数据传入和传出可能是一个大问题,您可能希望在代码中限制一些内容。
  • 限制 您的用户自己可能会遇到容器 /tmp 目录的限制。他们可能 运行 遇到最大负载 return 大小的问题。其他极限题可以研究here.

据我了解,您不能用代码破坏 Lambda 函数容器,这样以后的请求就不会 运行。我相信如果现有容器不存在,或者如果现有容器遇到一些问题,将启动一个新容器。

推荐

根据我刚刚做的一些额外研究,您可能希望在某种类型的上下文中 运行 您的 javascript 代码,这样您的用户只能访问节点 API 你想要的端点,以及你自己的系统定义的变量。使用像 safe-eval might work. Other people have asked the question of executing eval in context, such as here, or perhaps you could prepend use strict; and define your function variables each time you call eval, such as described here.

这样的工具

此外,在您关闭函数和 return 您的 stderrstdout 之前,您可以清除 /tmp 目录。我担心的是,如果 AWS 在同时执行两个请求时使用同一个容器,并且您删除了同时执行的两个函数的 "scratch" space。在我的研究中,我一直没能找到决定性的答案。

此时我仍然要说,您的 /tmp 目录是您要 运行 解决最潜在问题的地方。如果你能想出一种方法来限制对单个目录的使用而不能导航到父目录,或者 AWS 是否会在另一个函数中使用它的同时重用一个容器,那么我认为你可以使用Lambda 执行 user-provided 代码而不用太担心。或者,您可以通过从您使用的任何上下文方法中排除它来阻止您的用户访问您的文件系统。

来自 AWS 的关于容器重用的额外阅读是 here

请记住,除了 eval() 之外,您还可以 运行 通过在 nodejs 运行 时间内实例化匿名函数来编写任意代码。

这将为您带来额外的好处,即 能够从您的输入代码 中获得 return 值,而无需使用 child_process.

// POST https://g0623a10zf.execute-api.ap-southeast-2.amazonaws.com/prod/exec-script
// Content-Type: text/plain
//
// return process.env;

exports.handler = (event, context, callback) => {
    var err = '', result;
    try {
        // ie: result = new Function('return process.env;')();
        result = new Function(event.body)(); 
    } catch (e) {
        console.log(err = e);
    }
    context.succeed({
        statusCode: err ? '500' : '200',
        body: err || result
    }); 
    // Terminate runtime
    callback(err, result);
};

显然,您仍然有@forrestmid 提出的所有安全问题(以及其他一些问题),但您可以跳过写入文件然后执行文件的部分。

还有一个额外的好处,即这种方法不允许 require 调用 nodejs 模块,这将消除您的很多头痛。 IE。每个 lambda 都是在预装 aws-sdk 模块的情况下执行的,因此恶意用户可以通过首先执行 require('aws-sdk').

来寻找利用任何不安全 AWS 资源的方法