跳到主要內容

安全性

在建構應用程式時,安全性經常被忽略。的確,要建構出完全堅不可摧的軟體是不可能的——我們還沒有發明出完全堅不可摧的鎖(畢竟,銀行金庫仍然會被闖入)。然而,成為惡意攻擊受害者或暴露於安全漏洞的機率,與您願意投入多少努力來保護您的應用程式免受任何此類事件的影響成反比。儘管普通的掛鎖可以被撬開,但它仍然比櫃子的掛鉤難以逾越得多!

在本指南中,您將學習有關儲存敏感資訊、身份驗證、網路安全以及有助於保護您的應用程式安全的工具的最佳實務。這不是一份起飛前檢查清單——而是一份選項目錄,其中的每個選項都將有助於進一步保護您的應用程式和使用者。

儲存敏感資訊

永遠不要在您的應用程式程式碼中儲存敏感的 API 金鑰。應用程式套件中包含的任何內容都可能被任何檢查應用程式套件的人以純文字形式存取。諸如 react-native-dotenvreact-native-config 之類的工具非常適合新增特定於環境的變數,例如 API 端點,但不應將它們與伺服器端環境變數混淆,伺服器端環境變數通常可能包含機密和 API 金鑰。

如果您必須擁有 API 金鑰或機密才能從您的應用程式存取某些資源,則最安全的方法是在您的應用程式和資源之間建立一個協調層。這可能是一個無伺服器函數(例如,使用 AWS Lambda 或 Google Cloud Functions),它可以轉發包含所需 API 金鑰或機密的請求。API 消費者無法以存取應用程式程式碼中機密資訊的相同方式存取伺服器端程式碼中的機密資訊。

對於持久化的使用者資料,請根據其敏感性選擇正確的儲存類型。 隨著您的應用程式被使用,您通常會發現需要將資料儲存在裝置上,無論是為了支援您的應用程式離線使用、減少網路請求,還是儲存使用者的存取權杖在會話之間,以便他們不必每次使用應用程式時都重新驗證身份。

持久化與非持久化 — 持久化資料會寫入裝置的磁碟,這讓您的應用程式可以在應用程式啟動後讀取資料,而無需執行另一個網路請求來擷取它或要求使用者重新輸入它。但這也可能使該資料更容易受到攻擊者的存取。非持久化資料永遠不會寫入磁碟——因此沒有資料可存取!

Async Storage

Async Storage 是一個社群維護的 React Native 模組,它提供非同步、未加密的鍵值儲存。Async Storage 不在應用程式之間共享:每個應用程式都有自己的沙箱環境,並且無法存取來自其他應用程式的資料。

適合在以下情況使用 async storage...不適合使用 async storage 於...
跨應用程式執行持久化非敏感性資料權杖儲存
持久化 Redux 狀態機密資訊
持久化 GraphQL 狀態
儲存全域應用程式範圍的變數

開發者筆記

Async Storage 是網頁中 Local Storage 的 React Native 等效物

安全儲存

React Native 沒有捆綁任何儲存敏感資料的方式。但是,Android 和 iOS 平台已經有現有的解決方案。

iOS - Keychain Services

Keychain Services 允許您安全地儲存使用者的少量敏感資訊。這是儲存憑證、權杖、密碼和任何其他不屬於 Async Storage 的敏感資訊的理想場所。

Android - Secure Shared Preferences

Shared Preferences 是 Android 上用於持久性鍵值資料儲存的等效物。Shared Preferences 中的資料預設未加密,但 Encrypted Shared Preferences 包裝了 Android 的 Shared Preferences 類別,並自動加密金鑰和值。

Android - Keystore

Android Keystore 系統可讓您將加密金鑰儲存在容器中,使其更難從裝置中提取。

為了使用 iOS Keychain Services 或 Android Secure Shared Preferences,您可以自行編寫橋接器,或者使用為您包裝它們並以您自己的風險提供統一 API 的程式庫。一些值得考慮的程式庫

請注意不要無意中儲存或暴露敏感資訊。 這可能會意外發生,例如將敏感表單資料儲存在 redux 狀態中,並將整個狀態樹持久化在 Async Storage 中。或將使用者權杖和個人資訊傳送到應用程式監控服務,例如 Sentry 或 Crashlytics。

身份驗證和深度連結

行動應用程式有一個網路中不存在的獨特漏洞:深度連結。深度連結是一種從外部來源將資料直接傳送到原生應用程式的方式。深度連結看起來像 app://,其中 app 是您的應用程式方案,而 // 後面的任何內容都可以在內部用於處理請求。

例如,如果您正在建構電子商務應用程式,您可以使用 app://products/1 深度連結到您的應用程式並開啟產品 ID 為 1 的產品詳細資訊頁面。您可以將這些視為網路上 URL 的類型,但有一個關鍵區別

深度連結不安全,您永遠不應在其中傳送任何敏感資訊。

深度連結不安全的原因是因為沒有集中式方法來註冊 URL 方案。作為應用程式開發人員,您可以透過在 iOS 的 Xcode 中配置它 或在 Android 上新增意圖,來使用您選擇的幾乎任何 URL 方案。

沒有什麼可以阻止惡意應用程式透過也註冊到相同的方案來劫持您的深度連結,然後獲得對您的連結包含的資料的存取權限。傳送類似 app://products/1 的內容是無害的,但傳送權杖是一個安全問題。

當作業系統有多個應用程式可供選擇以開啟連結時,Android 將向使用者顯示一個歧義對話方塊,並要求他們選擇要使用哪個應用程式來開啟連結。然而,在 iOS 上,作業系統會為您做出選擇,因此使用者將毫不知情。Apple 已採取措施在後來的 iOS 版本(iOS 11)中解決此問題,他們在其中制定了先到先得的原則,儘管這種漏洞仍然可以透過不同的方式利用,您可以在這裡閱讀更多相關資訊。使用 通用連結 將允許在 iOS 中安全地連結到您應用程式內的內容。

OAuth2 和重新導向

OAuth2 身份驗證協定如今非常流行,被譽為最完整和最安全的協定。OpenID Connect 協定也基於此。在 OAuth2 中,系統會要求使用者透過第三方進行身份驗證。成功完成後,此第三方會重新導向回請求應用程式,並帶有一個驗證碼,該驗證碼可以交換為 JWT — JSON Web Token。JWT 是一種開放標準,用於在網路上安全地傳輸各方之間的資訊。

在網路上,此重新導向步驟是安全的,因為網路上的 URL 保證是唯一的。對於應用程式來說,情況並非如此,因為如前所述,沒有集中式方法來註冊 URL 方案!為了解決此安全問題,必須以 PKCE 的形式新增額外的檢查。

PKCE,發音為“Pixy”,代表 Proof of Key Code Exchange(金鑰碼交換證明),是 OAuth 2 規範的擴充。這涉及新增額外的安全層,以驗證身份驗證和權杖交換請求是否來自同一用戶端。PKCE 使用 SHA 256 加密雜湊演算法。SHA 256 為任何大小的文字或檔案建立唯一的「簽名」,但它是

  • 始終具有相同的長度,無論輸入檔案如何
  • 保證始終為相同的輸入產生相同的結果
  • 單向的(也就是說,您無法對其進行逆向工程以揭示原始輸入)

現在您有兩個值

  • code_verifier - 用戶端產生的大型隨機字串
  • code_challenge - code_verifier 的 SHA 256

在初始 /authorize 請求期間,用戶端也會傳送其在記憶體中保留的 code_verifiercode_challenge。在授權請求正確傳回後,用戶端也會傳送用於產生 code_challengecode_verifier。然後,IDP 將計算 code_challenge,查看它是否與最初的 /authorize 請求中設定的內容相符,並且僅在值相符時才傳送存取權杖。

這保證只有觸發初始授權流程的應用程式才能成功地將驗證碼交換為 JWT。因此,即使惡意應用程式獲得對驗證碼的存取權限,它本身也將毫無用處。要查看實際操作,請查看此範例

一個值得考慮用於原生 OAuth 的程式庫是 react-native-app-auth。React-native-app-auth 是一個用於與 OAuth2 提供者通訊的 SDK。它包裝了原生 AppAuth-iOSAppAuth-Android 程式庫,並且可以支援 PKCE。

只有在您的身份提供者支援 PKCE 的情況下,React-native-app-auth 才能支援 PKCE。

OAuth2 with PKCE

網路安全

您的 API 應始終使用 SSL 加密。SSL 加密可防止請求的資料在離開伺服器到到達用戶端之前以純文字形式被讀取。您會知道端點是安全的,因為它以 https:// 而不是 http:// 開頭。

SSL Pinning

使用 https 端點仍然可能使您的資料容易受到攔截。使用 https,用戶端只有在伺服器可以提供由預先安裝在用戶端上的受信任憑證授權單位簽署的有效憑證時,才會信任伺服器。攻擊者可以透過在使用者的裝置上安裝惡意根 CA 憑證來利用這一點,因此用戶端將信任所有由攻擊者簽署的憑證。因此,僅僅依賴憑證仍然可能使您容易受到中間人攻擊

SSL pinning 是一種可以在用戶端使用的技術,以避免這種攻擊。它的工作原理是在開發期間將受信任憑證的清單嵌入(或釘選)到用戶端,以便只接受使用受信任憑證之一簽署的請求,並且任何自簽憑證都不會被接受。

使用 SSL pinning 時,您應該注意憑證到期。憑證每 1-2 年到期一次,當憑證到期時,需要在應用程式和伺服器上更新憑證。一旦伺服器上的憑證已更新,任何嵌入舊憑證的應用程式都將停止運作。

總結

沒有萬無一失的方法來處理安全性,但透過有意識的努力和勤奮,可以顯著降低您的應用程式中發生安全漏洞的可能性。根據儲存在您的應用程式中的資料的敏感性、使用者數量以及駭客在獲得對其帳戶的存取權限時可能造成的損害,按比例投資於安全性。並記住:存取從未被請求的資訊要困難得多。