在AWS Lambda中使用JavaScript eval()

我想使用AWS Lambda运行用户通过Web提交的JavaScript代码。 我的Lambda函数会返回返回值stderrstdout 。 我可以遇到什么问题?

恶意用户是否能够提交导致Lambda函数问题的代码? 执行用户所做的更改,例如对于节点环境或文件系统,是否在整个调用中保持不变? 有什么办法可以防止呢?

而不是eval()我可以将文件写入Lambda文件系统并调用:

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

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

  • 文件系统 – 您的用户可以提交可以对您的文件系统进行更改的代码,这些更改将在重用容器上调用的函数之间持续存在。 并不是所有的用户代码请求都会在同一个容器上运行,但是如果两个请求靠近在一起,则同一个容器和相同的“scratch”磁盘空间将被访问。 一种潜在的阻止方法是,如果可以将包含的函数(如fs限制为在函数调用时定义的特定目录,那么可以为/tmp目录中的每个请求创build一个随机目录。 我不确定这是否可能。 你必须确保用户不能再自己需要fs 。 更好的build议是使用safe-eval类的东西,稍后我会进一步讨论。
  • 环境variables – eval中运行的代码可以访问Node环境variables。 如果您需要向数据库,其他AWS产品等发出任何请求,那么您需要为Lambda函数提供策略,除非您对证书进行了硬编码,并且该信息可以由用户代码访问。 您必须确保您提供给系统的任何凭据不在环境variables中,如果是,那么您的用户可以访问它们。
  • 节stream – 您可能遇到节stream问题。 默认情况下,AWS只允许100个并发调用。 如果您不限制用户提交自己的JS的请求,那么您可能遇到由于达到限制而被拒绝的请求。 我最近要求增加一个限制,我能够把我的请求增加到3000个并发请求,而不需要评估他们当前的基础设施是否可以处理它,所以他们可能也没有问题。
  • 费用 – 我相信你已经考虑过函数超时了,但是你要确保你限制了函数的运行时间,这样你的用户不会损坏你的AWS账单。 此外,它可能看起来很小,但大规模的一堆6MB的请求响应加起来,所以数据传入和传出可能是一个大问题,你可能想限制在你的代码。
  • 限制您的用户可能会遇到容器/tmp目录。 他们可能遇到最大有效负载返回大小的问题。 其他限制问题可以在这里研究。

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

build议

基于我刚刚做的一些额外的研究,您可能希望在某种types的上下文中运行您的JavaScript代码,以便您的用户只能访问您希望它们的Node API端点以及您自己的系统定义的variables。 使用像safe-eval这样的工具可能会起作用。 其他人问过在上下文中执行eval的问题,比如这里 ,或者你可以预先use strict; 并在每次调用eval时定义函数variables,如此处所述。

另外,在closures这个函数并返回stderrstdout你可以清除你的/tmp目录。 我关心的是,如果AWS在同时执行两个请求时使用相同的容器,并删除同时执行的两个函数的“scratch”空间。 在我的研究中,我还没有find一个决定性的答案。

在这一点上我仍然会说,你的/tmp目录是你将要遇到的最有潜力的问题。 如果您可以想出一种方法来限制使用到单个目录而不能导航到父目录,或者AWS将在另一个函数中同时使用容器,那么我认为您可以使用Lambda可以执行用户提供的代码,而不用太担心。 或者,您可以通过将您的用户从所使用的任何上下文方法中排除,从而防止用户访问您的文件系统。

来自AWS的关于容器重用的更多信息在这里 。

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

这将使您无需使用child_process即可从input代码中返回值 ,从而获得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模块,这将带走很多你的头痛。 也就是说,每个lambda都是预先安装的aws-sdk模块执行的,因此恶意用户可以通过首先执行require('aws-sdk')来查看利用任何不安全的AWS资源的方法。