前言
from anquanke.com
近些年来,越来越多的IT产业正在向云原生的开发和部署模式转变,这些模式的转变也带来了一些全新的安全挑战。
对象存储作为云原生的一项重要功能,同样面临着一些列安全挑战。但在对象存储所导致的安全问题中,绝大部分是由于用户使用此功能时错误的配置导致的。据统计,由于缺乏经验或人为错误导致的存储桶错误配置所造成的安全问题占所有云安全漏洞的16%。
以2017美国国防部承包商数据泄露为例:此次数据泄露事件是由于Booz Allen Hamilton公司(提供情报与防御顾问服务)在使用亚马逊S3服务器存储政府的敏感数据时,使用了错误的配置,从而导致了政府保密信息可被公开访问。经安全研究人员发现,公开访问的S3存储桶中包含47个文件和文件夹,其中三个文件可供下载,其中包含了大量“绝密”(TOP SECRET)以及“外籍禁阅”(NOFORN)文件。
与此相似的案例有很多,例如Verizon数据泄露事件、道琼斯客户数据泄露事件。如何正确的使用以及配置存储桶,成为了云上安全的一个重要环节。
存储桶的访问控制包含多个级别,而每个级别都有其独特的错误配置风险。在本文中,我们将深入探讨什么是存储桶、什么是存储桶ACL、什么是存储桶Policy以及平台是如何处理访问权限,并对错误配置存储桶权限导致的安全问题进行阐述。通过本文的阅读,可以很好的帮助理解存储桶的鉴权方式以及鉴权流程,避免在开发过程中产生由存储桶错误配置导致的安全问题。
首先,我们来看简单的对对象存储的概念进行了解。
对象存储
在了解对象存储之后,我们来梳理下ACL、Policy、存储桶的鉴权方式以及鉴权流程以及使用过程中容易产生的配置错误。
存储桶访问权限(ACL)
访问控制列表(ACL)使用 XML 语言描述,是与资源关联的一个指定被授权者和授予权限的列表,每个存储桶和对象都有与之关联的 ACL,支持向匿名用户或其他主账号授予基本的读写权限。ACL属性见下表:
从控制台上来看,存储桶访问权限分为公共权限与用户权限,见下图:
从上图的选项来看,公共权限和用户权限配置共同组成了存储桶访问权限。公共权限包括私有读写、公有读私有写和公有读写这几个选项可以选择,且为单选:
用户权限可以通过添加用户进行配置,通过填写账号ID并为其配置数据读取、数据写入、权限读取、权限写入以及完全控制五个选项。
图 3用户权限选项
除完全控制选项,其他几个选项都可以灵活的搭配;而勾选完全控制选项后,则会将前四个选项一同勾选上。
控制台中存储桶公共权限以及用户权限可配置项如下:
存储桶访问权限 | ||
公共权限 | 用户权限 | |
私有读写 | √ | |
公有读私有写 | √ | |
公有读写 | √ | |
数据读取 | √ | |
数据写入 | √ | |
权限读取 | √ | |
权限写入 | √ | |
完全控制 | √ |
表格 2存储桶访问权限
但是公共权限与用户权限有什么区别与关联呢?二者又是如何作用于访问控制列表(ACL)呢?
这些问题,单从控制台上功能上来看是并不能完全理解的,我们需要通过修改控制台中不同的公共权限与用户权限组合,对比ACL中内容的变化来分析控制台上这些配置项的真实作用。
首先我们通过在控制台中勾选的选项来测试一下公共权限是如何作用于ACL的。
公共权限
公共权限包括:私有读写、公有读私有写和公有读写,我们将依次测试一下在控制台中勾选后ACL中实际的配置情况。
私有读写
只有该存储桶的创建者及有授权的账号才对该存储桶中的对象有读写权限,其他任何人对该存储桶中的对象都没有读写权限。存储桶访问权限默认为私有读写。
我们将公共权限设置为私有读写,见下图:
通过访问API接口,获取此时存储桶ACL规则:
如上所示,ACL 描述了存储桶拥有者(Owner)为(用户 UIN:10001xxx),且此用户拥有存储桶的完全控制权限(FULL_CONTROL)。
值得注意的是,此处XML中权限配置,并不是因为我们勾选了公共权限配置中的私有读写而来,而是控制台中用户权限里默认配置中当前账号的权限策略,见下图红框处:
因此,在公共权限里勾选私有读写,相当于在ACL中不额外写入任何配置内容。
公有读私有写
任何人(包括匿名访问者)都对该存储桶中的对象有读权限,但只有存储桶创建者及有授权的账号才对该存储桶中的对象有写权限。
我们将公共权限设置为公有读私有写,见下图:
通过访问API接口,获取此时存储桶访问权限(ACL)
从XML内容可见,通过勾选公有读私有写,ACL中新增了如下配置条目:
此条配置授予了AllUsers用户组的READ的权限,按权限分类来说,属于“匿名用户公有读”权限,示意图如下:
公有读写
任何人(包括匿名访问者)都对该存储桶中的对象有读权限和写权限。
通过访问API接口,获取此时存储桶ACL。
如上所示,通过勾选公有读写,ACL中新增了如下配置条目。
与上文的公有读私有写权限相比,新增了AllUsers用户组WRITE的权限,即“匿名用户公有读写”权限,示意图如下:
从上述实验结果来看:公共权限配置的选项“私有读写“、”公有读私有写“和公有读写”本质上是在ACL中添加AllUsers用户组的READ与WRITE权限。公共权限配置选项的总结如下:
- 私有读写:不在ACL中添加任何额外的权限配置条目
- 公有读私有写:在ACL中添加AllUsers用户组READ权限项
- 公有读写:在ACL中添加AllUsers用户组READ权限项、AllUsers用户组WRITE权限项
在分析完公共权限之后,我们来分析一下用户权限。
用户权限
用户权限和公共权限有什么区别呢?其实都是修改ACL策略,没有本质的区别,只是公共权限在勾选时,生成ACL中ALLUSers的三个权限,而通过用户权限配置的,在ACL中精准到用户,并且权限策略也扩充为5个可选项。
我们先保持公共权限的默认设置——私有读写,并在控制台编辑用户权限,添加一个ID为123456的账号。
数据读取-数据写入
我们为此账号设置数据读取、数据写入的权限,见下图:
通过访问API接口,获取此时存储桶ACL。
从XML内容可见,在控制台新增一个拥有数据读取、数据写入权限的账号后, ACL中新增了如下配置:
ACL中增加了一个uin为123456的用户的READ与WRITE权限,示意图如下:
权限读取-权限写入
接下来我们保持公共权限为默认的私有读写不变,并在用户权限处添加一个ID为123456的账号,我们为此账号设置权限读取、权限写入的权限,见下图:
通过访问API接口,获取此时存储桶ACL。
如上所示,在控制台新增一个拥有权限读取、权限写入的账号后, ACL中新增了如下配置:
ACL中增加了一个uin为123456的用户的READ_ACP与WRITE_ACP权限,此时123456用户可以对ACL进行读取以及更新操作,示意图如下:
公有读写-数据读取-数据写入
在这环节中,我们将实验一下公共权限与用户权限的关系,我们将公共权限设置为公有读写,并在用户权限处添加一个ID为123456的账号,我们为此账号设置权限读取、权限写入的权限,见下图:
通过访问API接口,获取此时存储桶ACL
通过对比公共权限章节中公有读写与用户权限章节中数据读取-数据写入部分的内容可以发现, 在控制台中配置的公共权限与用户权限是各自作用于ACL中,在ACL中并不互相影响,配置的公有读写将会在ACL中添加一个AllUsers用户组的WRITE与READ权限,而用户权限中添加的123456账号的数据读取、数据写入将在 ACL中加入了一个123456账号的READ与WRITE权限。
但是细心的读者可能会发现一个有意思的问题,在配置用户权限时,ACL中默认的Owner的FULL_CONTROL权限不见了
消失的Owner权限
对比一下公共权限章节中私有读写部分的ACL,我们发现了一个问题,见下图:
虽然我们仅仅是在用户权限处增加了一个新用户,并没有删除也没有办法删除控制台中默认的主账号的完全控制权,但是ACL中默认的拥有完全控制权的主账号条目不见了,见上图红框处。
这样会不会导致主账号失去了存储桶的控制权呢?经过测试发现,主账号依然拥有存储桶的完全控制权,这是问什么呢?
通过查阅官方文档,我们发现了答案:
资源的拥有者,即Owner始终对资源具备完全控制权,无论ACL中是否存在此项。
存储桶策略(Bucket Policy)
在分析完ACL之后,我们来看看Policy。存储桶策略(Bucket Policy)使用 JSON 语言描述,支持向匿名身份或任何 CAM 账户授予对存储桶、存储桶操作、对象或对象操作的权限,在对象存储中存储桶策略可以用于管理该存储桶内的几乎所有操作。Policy属性见下图:
我们可以通过在控制台中添加策略的方式来设置Policy权限。
我们添加一个新策略,该策略允许所有用户对我们的存储桶进行所有操作,见下图:
通过访问API接口,获取权限策略。
可以发现,Policy中以共有四个主要的属性:Action、Effect、Principal、Resource,分别对应了控制台中填写的操作、效力、用户、资源路径。与ACL仅可以配置的用户与权限选项相比,控制的颗粒更细。
接下来,我们添加一个允许账号ID为123456的账号对cos-aclxxx/policy_test路径的读操作。
通过访问API接口,获取权限策略。
在这个Policy中,我们可以看到更细腻的Action与Resource配置。
对象访问权限
在对象存储中,每一个对象同样存在着可配置的访问权限,默认继承存储桶的ACL。
我们将此对象设置为公有读私有写权限,见下图:
通过查询GetObjectAcl API接口,获取其ACL。
从ACL可见,与存储桶的ACL配置项完全一样,只不过这里的ACL作用于目标对象而存储桶ACL作用于存储桶。
但是对象存储是如何通过ACL与Policy共同协调控制存储桶权限的呢?
我们接下来看一下对象存储的访问策略评估流程。
访问策略评估机制
在开始介绍对象存储访问策略评估流程之前,我们先介绍一下几个流程中涉及到的重要概念:显示拒绝、显示允许、隐式拒绝以及三者之间的联系:
在用户策略、用户组策略、存储桶 Policy 中针对特定用户有明确的 Deny 策略。
在用户策略、用户组策略、存储桶 Policy、存储桶 ACL 中通过grant-\*明确指定特定用户针对特定用户有明确的 Allow 策略。
在默认情况下(未经配置的情况下),所有请求都被隐式拒绝(deny)。
显示拒绝、显式允许、隐式拒绝之间的关系如下:
如果在用户组策略、用户策略、存储桶策略或者存储桶/对象访问控制列表中存在显式允许时,将覆盖此默认值。任何策略中的显式拒绝将覆盖任何允许。
在计算访问策略时,应取基于身份的策略(用户组策略、用户策略)和基于资源的策略(存储桶策略或者存储桶/对象访问控制列表)中策略条目的并集,根据显示拒绝、显式允许、隐式拒绝之间的关系计算出此时的权限策略。
图 22存储桶鉴权流程
通过上图,我们可以很清楚的理解存储桶的鉴权流程。
访错误配置导致的安全问题
错误使用公有读写权限
通过上文的分析可知,公有读权限可以通过匿名身份直接读取用户存储桶中的数据,存在着严重的安全隐患。
但是有些用户为了避免使用繁杂且细粒度的权限配置,会错误的将其存储桶设置为公有读写,这将导致了其存储桶中的内容被攻击者窃取与篡改。正如本文前言中所描述的2017美国国防部承包商数据泄露案例。即便是美国国防部承包商,在使用存储桶进行对象存储时,也会犯下这样的常见错误。
因此,为了保障存储桶安全,建议用户为存储桶配置私有读写权限。
存储桶、对象访问权限差异性问题
存储桶权限与对象权限的差异性,往往会为对象资源来安全性问题。
在实际操作中,为了存储桶的安全起见,存储桶的公共权限往往会被设置为私有读写,这也是存储桶的默认公共权限配置,见下图:
存储桶的私有权限表明,只有该存储桶的创建者及有授权的账号才对该存储桶中的对象有读写权限,其他任何人对该存储桶中的对象都没有读写权限。
但是将存储桶的公共权限设置为私有读写可以完全保护存储桶中的中的对象资源不被读取吗?
在我们测试的这个存储桶中,并未设置Policy策略,并且存在着一个名为p2.png的对象。
而从上文可知,存储桶中的对象也有着其对应的对象权限。
在这里我们将对象p2.png的ACL权限设置为公有读私有写,见下图:
通过访问p2.png资源url可以发现,此时p2.png对象可以被访问,见下图:
测试表明,当存储桶公共权限设置为私有读写时,当存储桶中的对象公共权限为公有读私有写时,此对象依然是可以被读取的。
实际原理很简单,我们为对象p2.png设置了公有读私有写ACL策略,此时对象资源p2.png的ACL如下:
根据上文访问策略评估机制一章可知,对象p2.png设置了AllUsers用户组的显性允许READ权限,因此当匿名用户访问p2.png时,即使存储桶设置了私有读写权限,依然可以访问此对象,原理图见下图:
因此,单单依靠存储桶的访问权限,并不能保护其中资源的未授权访问情况。为存储桶中资源配置对应的访问权限,才可以保证对象的安全性。
错误授予的操作ACL权限
在Policy权限设置中,如果授权用户操作存储桶以及对象ACL的权限(GET、PUT)见下图:
即使Policy中没有授权该用户读取存储桶、写入存储桶、读取对象、写入对象的权限,这个操作依然是及其危险的,因为该用户可以通过修改存储桶以及对象的ACL进行越权。
我们在coscmd中配置授权用户的密钥信息后,通过coscmd list列出存储桶中内容。
从返回结果来看,该用户并没有读取存储桶列表的权限
经过测试,用户同样也没有下载p2.png对象的权限,见下图:
但是我们却可以查询存储桶中对象的ACL,见下图:
由于该用户拥有修改存储桶中对象ACL的权限,因此可以通过如下指令授予该用户读取p2.png的权限,见下图:
在修改过p2.png权限之后,可以顺利的将此对象下载到本地。
资源超范围限定
在使用存储桶进行对象读取或写入操作时,如果没有合理的或者错误的在Policy中配置用户允许访问的资源路径(resource),则会出现越权访问,导致用户数据被恶意上传覆盖或被其他用户下载等安全问题。
在Web应用开发中,经常会发生此类问题。设想以下场景:在一个Web应用使用对象存储来存储用户头像,且通过前端直传的方式将用户上传的头像传至存储桶中,并希望在存储桶/avatar/路径中存储桶用户的头像,由于后端开发时为了方便而进行了不规范的存储桶Policy配置,在生成用户用以上传头像的临时密钥时直接将此临时密钥允许访问的 resource 指定为 qcs::cos:<Region>:uid/<APPID>:<BucketName-APPID>/avatar/*路径。
这样以来,系统为每个用户所生成的用以上传以及浏览头像的临时密钥虽然不尽相同,但是这个临时密钥都拥有qcs::cos:<Region>:uid/<APPID>:<BucketName-APPID>/avatar/*路径中的所有资源的读写权限。
这一错误的配置导致了很多严重的安全问题,由于在此场景下,Web应用程序使用前端直传的方式访问存储桶,因此后台生成的临时密钥将会发送给前台,任意用户通过网络抓包等手段获取到的临时凭据,可参见下图流量中响应包内容。
在获取了临时密钥之后,攻击者凭借此凭据读写qcs::cos:<Region>:uid/<APPID>:<BucketName-APPID>/avatar/*路径中的任意对象。
攻击者可以通过此方式覆盖目录中其他用户资源,见下图:
上图攻击者通过test.txt文件覆盖了16.png。当然,攻击者也可以轻易的读取此目录中其他用户的文件。
针对此问题的修复方式如下:可以通过每个用户的用户标识来为每一个用户设置一个独用的路径,例如可以在为用户生成临时密钥时,将policy中resource 指定为 qcs::cos:<Region>:uid/<APPID>:<BucketName-APPID>/avatar/<Username>/*来满足规范要求;此外,resource 字段支持以数组的形式传入多个值。因此,也可以显式指定多个 resource 值来完全限定用户有权限访问的最终资源路径。
写在后面
对象存储服务作为一项重要的云上服务,承担了存储用户数据的重要功能。从上文分析可见,对象存储服务提供了细粒度的访问权限控制功能,以保证用户数据的安全性。
但是由于用户使用对象存储服务时安全意识不足或对访问权限以及访问策略评估机制错误的理解,将会导致数据被非法访问或篡改。这些错误的配置包括用户错误的使用公有读写权限、错误授予操作ACL权限、配置资源超过范围限定以及对存储桶权限机制错误理解等,这些错误的配置将会造成严重的安全问题。
因此,深入了解对象存储服务所提供的访问权限以及访问策略评估机制,并始终遵循最小权限原则,将会为存储桶中存储的数据安全构筑立体防护体系的一道坚固的门锁,与此同时,也可以通过检查存储桶日志以及文件时间戳来排查存储桶是否被侵害,确保云上资产的安全。
参考文献
A deep dive into AWS S3 access controls – taking full control over your assets
https://blog.lightspin.io/how-to-access-aws-s3-buckets
https://blog.lightspin.io/risks-of-misconfigured-s3-buckets
https://cloud.tencent.com/document/product/436/40265
https://main.qcloudimg.com/raw/document/intl/product/pdf/436_9511_zh.pdf
请登录后发表评论
注册