用于前端直传COS的临时密钥安全指引
最近更新时间: 2024-10-17 17:10:00
简介
在移动应用和 Web 应用中,您可以通过 iOS/Android/JavaScript SDK 在前端直接向 COS 发起请求,此时数据的上传和下载可以不经过您的后端服务器,既节约了您后端服务器的带宽和负载,还可以充分利用 COS 的带宽和全球加速等能力,提升您的应用体验。
在实际应用中,您需要使用临时密钥作为前端的 COS 请求签名,以防止永久密钥的泄漏及越权访问等问题。然而,即便是使用临时密钥,如果您在生成临时密钥时指定了过大的权限或路径,那么同样有可能发生越权访问等问题,将对您的应用带来一定的风险,本文重点介绍了部分风险案例,以及您应当遵守的安全规范,以便让您的应用能够安全的使用 COS。
前提条件
本文假定您已经充分理解临时密钥的相关概念,能够生成及使用临时密钥来请求 COS。有关临时密钥的生成及使用指引,请参见 临时密钥生成及使用指引 文档。
反面案例与安全规范
反面案例一:资源(resource)超范围限定
应用 A 在注册用户上传头像中使用到了 COS,每个注册用户的头像拥有固定的对象键app/avatar/<Username>.jpg
,同时还会包含头像的不同尺寸,对应的对象键分别为app/avatar/<Username>_m.jpg
和app/avatar/<Username>_s.jpg
,后端为了方便使用,在生成临时密钥时直接将 resource 指定为<BucketName-APPID>/app/avatar/*
,此时恶意用户通过网络抓包等手段获取到生成的临时密钥后,可以覆盖上传任何用户的头像,产生越权访问,用户的合法头像数据被覆盖导致丢失。
安全规范
resource 代表临时密钥所允许访问的资源路径,此时需要充分考虑该路径所覆盖的最终用户,原则上 resource 所指定的资源要求仅能被单一用户所使用。此案例中指定的<BucketName-APPID>/app/avatar/*
显然会覆盖所有用户,因此存在安全漏洞。
在该案例中,可以考虑将用户的头像路径修改为app/avatar/<Username>/<size>.jpg
,此时可以将 resource 指定为 <BucketName-APPID>/app/avatar/<Username>/*
来满足规范要求;此外,resource 字段支持以数组的形式传入多个值。因此,您也可以显式指定多个 resource 值来完全限定用户有权限访问的最终资源路径,例如:
"resource": [
"<BucketName-APPID>/app/avatar/<Username>.jpg",
"<BucketName-APPID>/app/avatar/<Username>_m.jpg",
"<BucketName-APPID>/app/avatar/<Username>_s.jpg"
]
反面案例二:操作(action)超范围限定
应用 B 提供一个公共的照片墙功能,所有照片均存放置在app/photos/*
下面,客户端同时需要列出对象(GET Bucket)和下载对象(GET Object)操作,后端为了方便使用,在生成临时密钥时直接将 action 指定为了name/cos:*
,此时恶意用户通过网络抓包等手段获取到生成的临时密钥后,可以对该资源路径下的任何对象执行所有对象操作(例如上传和删除等操作),产生越权访问,导致数据丢失,影响线上业务。
安全规范
action 代表临时密钥所允许请求的操作,原则上不允许使用name/cos:*
等允许所有操作的临时密钥下发至前端,必须明确列出所有需要用到的操作,同时如果各操作所需的资源路径不通,则需要操作与资源路径单独匹配,而不应合并处理。
在该案例中,应当使用"action": [ "name/cos:GetBucket", "name/cos:GetObject" ]
指明具体的操作。
反面案例三:资源与操作超范围限定
应用 C 提供一个管理工具,允许用户列出并下载所有人的文件(app/files/*
),但只能上传和删除个人目录下的文件(app/files/<Username>/*
),后端为了方便使用,在生成临时密钥时将2种权限,共4种操作(action)混合在一起。2种权限对应的资源路径也混合在一起,此时的临时密钥将具备资源路径中指定的更大权限,即用户可以列出、下载、上传和删除所有人的文件,恶意用户可以据此篡改或删除他人的文件,产生越权访问,用户的合法数据将暴露在风险之中。
安全规范
对于多个 action 与 resource 的组合,不应简单的将它们分别合并,而应当通过多个 statement 的形式组合在一起,避免简单的分别合并导致权限扩大化。
在该案例中,应当使用
"statement": [
{
"effect": "allow",
"action": [
"name/cos:GetBucket",
"name/cos:GetObject"
],
"resource": "<BucketName-APPID>/app/files/*"
},
{
"effect": "allow",
"action": [
"name/cos:PutObject",
"name/cos:DeleteObject"
],
"resource": "<BucketName-APPID>/app/files/<Username>/*"
}
]
反面案例四:越权获取临时密钥
应用 D 提供一个论坛应用,论坛的帖子附件保存在 COS 上,该论坛应用分为不同的用户级别,只有发帖数达到一定阈值的活跃用户才能看到某个版面内的帖子和附件,对于一些公共版面,所有用户均可以查看其中的帖子和附件。 在使用 COS 时,后端生成临时密钥的接口会根据前端传入的版面 ID ,生成允许下载对应版面附件的临时密钥。但在实现过程中,后端没有判断具体请求的用户是否有权限访问指定的版面 ID,导致任何人均可请求该接口获取可访问私密版面附件的临时密钥,产生越权访问,需要被限制访问的资源未能被有效限制,导致数据意外泄漏。
安全规范
对于获取临时密钥的接口,除了要保证获取到的临时密钥本身的权限在预期范围中,还应判断实际的业务场景,即正确的权限是否被正确的用户所请求,避免低权限用户拿到高权限的临时密钥。
在本案例中,后端接口还应该判断当前请求用户是否具备访问特定版面的权限,如无权访问则不允许返回临时密钥。
总结
以上几个案例说明了在预期外扩大临时密钥的权限可能导致的安全风险。由于前端直传 COS 时,恶意用户比较容易获取到临时密钥内容,因此该场景相对于通过后端访问 COS 来说,需要开发者们更加注重权限的控制。
本文提到的安全规范即最小权限原则,在实际应用中,根据 action 和 resource 可以枚举出所有可能的权限,例如3种 action 和2个 resource,可以计算出3 x 2=6个被允许访问的资源和对应的操作,您可以据此评估每一种情况是否符合预期,如超出预期权限范围,则应当考虑通过枚举多个 statement 的方式拆分权限。
此外,用于生成临时密钥的接口本身也要充分考虑身份认证和鉴权处理,只有获取临时密钥的行为是安全的,这样得到的临时密钥才能确保真正安全。安全链条上,不能有任何一处疏漏!