程序代码中的一个逻辑 bug 可能会引起数据错误、界面显示错乱,甚至是程序崩溃。做为开发者,谁摊上这事都恨不能赶忙修掉 bug,万不可拖到使用者们义愤填膺地来砸招牌。javascript
可若是是一个数据安全性的问题呢?使用者仍是能正常使用程序,没人知道这个问题的存在或隐患,包括开发者本身。直到某天使用者们忽然收到不少骚扰邮件,他们在应用中的数据被人恶意篡改,甚至所绑定的信用卡信息也被窃取……此刻,开发者才幡然悔悟——数据安全性问题可不像以前提到的那类 bug 那样容易搞定,即便堵上了漏洞,使用者的信息也仍是泄露了出去,被篡改的信息没那么容易恢复,经济上的损失更是难以估算。html
LeanCloud 做为一个将各项服务的 API 直接开放给客户端,并拥有数以万计开发者和海量应用的 BaaS 服务,势必要提供针对数据安全的各类保障机制。但更重要的是,只有开发者们了解并掌握了这些数据安全机制的用法,才能根据实际需求来有效地为应用数据加上防御措施。
java
访问 LeanCloud 的 API 须要提供应用的 AppID 和 AppKey,或是 AppID 和 MasterKey。node
当客户端须要直接访问 LeanCloud 提供的 API 时,开发者必须将 AppKey 编码到客户端的代码中。这样用户只要经过反编译客户端代码或者截获客户端发往服务器的请求,便可轻易获取 AppKey。数据库
所以千万不要认为:json
AppKey 是对数据的一种保护措施,只要不主动泄漏,就没人能得到 AppKey。后端
而是要明白:
AppKey 是公开的,须要结合 LeanCloud 提供的其余安全措施来保障应用数据的安全性!api
使用 MasterKey 来访问 LeanCloud API 会跳过所设置的任何安全措施,方便对数据进行强制性的修改。所以请尽可能保证 MasterKey 不会被泄漏,也不要将 MasterKey 放在任何客户端代码中,它只可出如今你的后端服务器或者云引擎的代码中。
安全
首先咱们要理解要用到的专有名词:Class 对应传统数据库中的表(Table),Object 对应表中的行记录(Row/Record),field 对应为列或字段。LeanCloud 能够分别给每一个 Class、每一个 Object 和每一个 field 设置不一样的访问权限,保证每位用户只能访问到被受权的数据。服务器
Class 权限只能够在控制台中进行设置,顾名思义,对应的是整个 Class 的读写权限。下图为 Class 的权限设置页面:
能够针对如下 4 种维度的用户分类来指定权限:
除了以上权限以外,每个 field 也有特殊的权限能够设置:
除了上面提到的 Class 权限,每每还须要针对具体的 Object 来设置不一样的权限。
在 LeanCloud 的数据存储服务中,每一个 Object 都会有一列特殊的属性 ACL (Access Control List) 来决定一个 Object 的实际读写权限。若是须要针对每一个 Object 设置不一样的读写权限,好比只容许做者修改本身发布过的文章,能够直接经过设置对应的 ACL 来实现。
能够设置在建立一个 Class 时新增长的 Object 的默认 ACL 值:
也能够在每一个 Object 上设置具体的 ACL:
若是有经过默认 ACL 值依然不能知足的状况,则能够考虑在插入数据时同时设置 ACL 权限:
$ curl -X POST \
-H "X-LC-Id: XEComQ0KWPD7r3RDQYrSCaPV-gzGzoHsz" \
-H "X-LC-Key: WFzTQJdI39iR6JJO9ytR290i" \
-H "Content-Type: application/json" \
-d '{"content": "Hello, world!","ACL": {"*": {"read": true, "write": false}, "58ce7493ac502e00589e3105": {"write": true}}}' \
https://api.leancloud.cn/1.1/classes/Post复制代码
除了直接使用 REST API 建立数据以外,其余各个语言的 SDK 也都提供了相应的方法来在建立对象时设置 ACL。
另外须要注意,若是一条数据的读写操做对应的 Object 与 Class 都设置了对应的权限,这时须要两个条件都知足,才能成功进行此次操做。而对于同一个请求,可能会有多个 ACL 设置,这时只要知足其中一项规则就能够执行此次操做了。好比不容许任何人(*)写入操做,可是容许特定的用户写入,这时这个用户是能够执行写入操做的。
上面提到的一些安全措施已经可以覆盖到大部分场景了,可是依然会有一些场景不能简单地经过设置权限来解决。典型的场景有:须要对非用户的维度进行权限或数据合法性校验。好比论坛,不容许在已经关闭的帖子下添加新的回复,彷佛使用上面提到过的机制很难作到这个限制。
这个时候就须要引入云引擎来完成需求了。云引擎是 LeanCloud 提供的另外一项服务,在之上除了像传统提供虚拟机的云服务同样,搭建本身的基于 HTTP 的网站服务以外,还能够结合数据存储服务,在读写数据时进行校验。
云引擎在进行数据读写时默认会使用 MasterKey 来进行鉴权,所以会拥有最高权限。不过此时由于代码运行在服务器上,不会被第三者篡改,所以只要合理使用,开发者没必要担忧安全性问题。
回到上面的问题,如何限制用户在已经关闭的帖子下,建立新的回复呢?能够考虑使用云引擎的 beforeSave 这个 hook 来进行检查:
@engine.before_save('Comment')
def before_comment_save(comment):
post = comment.get('post')
post.fetch() # post 是个 Pointer,所以须要先调用 `fetch()` 来获取数据
if post.get('isClosed'):
raise LeanEngineError('post is closed.')
# 除此以外,还能够对 Comment 作一些其余检查,好比是否有禁止出现的词汇复制代码
将这段代码部署在云引擎中,这样全部请求在已经关闭的帖子下建立回复的请求时都会被拒绝掉。
除了 beforeSave hook 以外,还有 beforeUpdate、beforeDelete 等等其余种类的 hook 来在对应的时机下执行,具体可参考 文档 。上述代码是使用 Python 来实现的,LeanCloud 还支持使用 Node.js、PHP、Java 来编写云函数。
再设想一种场景,假设咱们在 Post 表中使用一个叫作 commentsCount 的字段来保存帖子的回复数量,用于客户端展现。固然这个字段里的数据属于冗余,是能够直接经过查询 Comment 表中对应的数量来获取的,可是这就意味着在客户端中要展现 100 个帖子的列表时,须要再进行 100 次 API 调用来查询回复数量,而且底层数据库也会有响应的查询耗时,所以为了提高应用响应性能,通常状况下开发者都会设置这个冗余字段。
可是这时候问题来了,咱们如何确保这个字段的值是正确的呢?固然可让客户端每次发送完建立评论的请求后,再次发送一次请求去更新这个 commentsCount 字段。可是对于直接绕过了客户端、直接经过 curl 来发帖的用户,他们可能不会遵照「发完贴要更新回复数量」这个义务,甚至有人会直接设置一个错误的值进去,破坏应用数据的完整性,或带来其余影响,这时依然能够经过云引擎的 afterSave hook 来完成任务:
@engine.after_save('Comment')
def after_comment_save(comment):
post = comment.get('post')
post.increment('commentsCount', 1)
post.save()复制代码
除了将此段代码部署在云引擎,还须要结合上面的介绍,将 Post 表中的 commentsCount 设置为「只读」,这样就不用担忧有人会忘记更新评论数,以及故意破坏数据完整性的问题了。
此外,有时咱们还须要作更加复杂的权限设置,好比在用户查询数据时要过滤掉某些条件(只容许查询最近一个月建立的帖子),或者对相同 IP 发送的请求作频率限制,或者让某个没有权限的用户临时写入或查询某些数据等等。
这时能够经过云函数进行这种操做:
@engine.define('godApi')
def god_api(**params):
do_what_you_want_with_input_data(params)
return what_you_want()复制代码
事实上,将对应的数据权限所有关闭,只经过云函数来操做数据,使用 LeanCloud 进行应用开发,与本身搭建服务器来编写接口具备一样的数据安全性保障。
上面介绍了基于 LeanCloud 开发应用、保障数据安全的一系列措施,灵活性从弱到强。实际开发过程当中,开发者能够根据具体状况,选择相应的机制。
好比只对单一数据条目进行增删改查,而且只针对用户进行权限校验,彻底能够利用 Class / Object 权限设置来保障数据的安全性与统一性。除此以外的状况,则能够考虑将对应 Class 的读或写权限关闭,利用云引擎来进行安全性检查和数据校验。