程序代碼中的一個邏輯 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 的讀或寫權限關閉,利用雲引擎來進行安全性檢查和數據校驗。