做互联网产品绕不开使用存储服务,存取用户的照片、声音、影像等资源是最常见的功能了。你的产品是存储服务的使用者,无论使用云存储服务或是自建存储服务,安全值得你投资和反省,不吃亏。我分享一些经验。
摘要如下
- 使用私有空间存储用户数据
- 上传下载先向服务端申请
- 不要把私钥存在客户端
- 不要自己为上传的资源命名
使用私有空间存储用户数据
存储服务通常有公开和私有两种空间。
- 公开空间的资源通过固定URL下载,适合存放可公开下载的静态资源。
- 私有空间的资源每次下载都通过不同的URL,URL由存储服务提供的算法和私钥做过签名。
用户资源可由固定URL下载这显然不是安全的。
- URL被猜到的可能性很高,别人用程序扒你的资源库是迟早的事。
- URL被盗链太容易,你一点办法也没有。
私有空间要求你每次都生成URL来存取资源,只要存储服务给你的私钥不泄露,没人能猜到URL是什么。存储服务都会设计这样的安全机制来保护私有空间资源,并著文教你使用它,怕的只是你不懂。
上传下载先向服务端申请
上传下载资源的流量不走服务端是普遍共识。
但从客户端的上传和下载不应抛开服务端,而是应每次跟服务端申请,由服务端授予URL。好处
- 服务端知道每次上传和下载资源的事件,也容易建立关系数据
- URL是最新的,不会因为客户端版本多样而出现问题
- URL有时效、被签名,资源受保护不会被扒、被盗链
- 客户端不知道URL如何生成,不存放私钥
以手机 App + Api Server 的产品使用 Qiniu 云存储为例。
App 无论上传和下载都应当从 Api Server 请求URL,再直接访问 Qiniu 存取资源。如图
不要把私钥存在客户端
存储服务提供给你的私钥一定要保护,因为算法虽复杂却是公开的,私钥是保证签名有效的唯一手段。
你会把银行卡的背面写上密码还随手放吗?如果不会,那么为什么你架构产品时会把凭证放在客户端让大众拿到呢?
客户端没有秘密 。比如 Android App,拿到其的 apk 文件借助 apktool 这样的工具只需要跑一行命令就可以反编译。你存在其中的秘钥作为字符串可谓一览无余。
存放和使用私钥是服务端的职责,签名后URL交由客户端使用。
正确的姿势前文已经提到了,仍以手机 App + Api Server 的产品使用 Qiniu 云存储为例,App 无论上传和下载都应当从 Api Server 请求URL,再直接访问 Qiniu 存取资源。
用图解释得细致点
不要自己为上传的资源命名
听起来是反直觉的?其实是你不懂存储服务的设计。存储服务会对上传资源的按字节生成一个Key,作为其空间内的唯一标识。
如果你主动为其命名,那么云存储会按你说的办,用你的命名做这个Key。可你想过吗
- 你生成的Key科学吗?它会作为URL的一部分,至少要url safe的吧
- 你生成的Key唯一吗?即便是随机字符串也有碰撞的一天,那时你的资源会被覆盖
- 同样内容的上传资源只是命名不同,存为两份?那是两份存储空间的费用...
所以正确的姿势是,客户端上传完毕之后,存储服务会发回调请求到服务端,如果上传成功会带过去资源的Key,你应该在这时存下它。
[本文我首发于 https://wushaobo.github.io/2017/10/11/use-storage-service-safely.html]