原文連結 (自備梯子)
一些可以保護API key等資訊的簡單方法
幾乎所有 iOS 應用都需要使用一些私有值,比如 API key,HMAC secrets 或密碼。有一個簡單的途徑可以把這些私有值包含到你的 app 中,那就是把它們寫在程式碼裡或者寫在 Info.plist
檔案裡。但是這麼做會產生一個問題,就是這些檔案會被包含到 Git 倉庫裡,任何人都可以看到並獲取到原始碼,自然也就可以獲取到這些敏感資訊。
保護原始碼
針對上面提及的問題,首先想到的解決方式可能很簡單,就是直接保護原始碼,這樣其他人就沒法獲取到原始碼,也就沒辦法獲取到這些敏感資訊了。然而不幸的是很多應用是開源的,每個人都需要去獲取其原始碼,所以這種解決方式並不總是奏效的。對於一些頂級的 iOS app,人們還可以去反編譯它或者分析應用包的 Contents 來尋找這些敏感資訊。
讓檔案不包含到 Git 倉庫中
在允許保持程式碼開源的前提下,你可以簡單地讓包含敏感資訊的檔案被 Git 忽略。任何人需要在專案中使用這些敏感資訊,可以簡單地建立相關檔案。對於一個開源專案來說,這些相關細節,可以在 README 或者 wiki 頁面裡註明。
如果按照上面所說,那麼一個 iOS 應用,每一個開發者都需要建立一個原始碼檔案或者一個 plist 檔案,然後新增到專案中。這樣你不得不很小心地在專案設定裡去確保相關檔案的引用情況,即使當這些相關檔案不包含在 Git 中。
xcconfig
我更傾向於把敏感資訊儲存在 xcconfig
檔案中,每一個編譯環境( Debug 或 Release )對應一個檔案。這些檔案可以讓你在不同環境使用不同的配置,為開發者簡化配置,而且讓敏感資訊不包含在 Git 倉庫裡。
1. 建立一些樣例檔案在專案裡
我更喜歡把它們放在 BuildConfig
資料夾裡,讓它們與專案裡的其他檔案分離開。開發者只需要簡單地複製它們,給它們重新命名,然後把一些敏感資訊寫到裡面。這些樣例檔案可以指定 key 的列表,但是不把值包含進去。每一個編譯版本的配置,比如 Debug 和 Release,都需要這麼一個檔案。
debug.example.xcconfig
API_CLIENT_ID = API_CLIENT_ID_FOR_TEST
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_TEST
複製程式碼
release.example.xcconfig
API_CLIENT_ID = API_CLIENT_ID_FOR_PRODUCTION
API_CLIENT_SECRET = API_CLIENT_SECRET_FOR_PRODUCTION
複製程式碼
你將需要把這些檔案新增到 Xcode 專案中。但是你要確定把他們從 Xcode 的檔案檢查器的 Target Membership
區域中移除。
2. 為每一個編譯版本建立真實檔案
在這裡面把例子的值替換成專案裡真實的值
debug.xcconfig
API_CLIENT_ID = 123456789
API_CLIENT_SECRET = abcdefgh
複製程式碼
release.xcconfig
API_CLIENT_ID = 987654321
API_CLIENT_SECRET = hgfedcba
複製程式碼
你將要把這些檔案新增到專案中,但是它們不需要依附到任何 target。
3. 把這些配置檔案新增到 .gitignore
檔案中。
4. 在專案設定裡選中這些檔案。
你需要在 Info->Configurations
中選中這些檔案,在 Debug 和 Release 中都去設定它們。
值的使用
任何包含在 xcconfig 檔案的值都可以作為專案檔案的環境變數來使用,比如 Info.plist
,如果需要再程式碼裡使用這些值,你需要在 Info.plist
建立一個屬性連結到環境變數,使用這種格式 $(API_CONFIG_KEY)
。
2018-02-01 更新
把敏感資訊儲存在Info.plist
不是最安全的方式。如果你的應該比較注重安全性,那麼你可能需要把這些敏感資訊放在其他地方。本篇文章的側重點事把它們排除在 Git 倉庫之外。後面我會再更新說明如何在程式碼中使用這些環境變數,來作為 Info.plist
的代替方案。
2018-02-03 更新
使用 Sourcery,可以生成一個 Swift 結構體,該結構體包含了在 xcconfig 檔案裡的特定敏感資訊。
-
把 Sourcery 新增到專案中。
-
建立一個 stencil 檔案,例如 AppSecrets.stencil,把專案程式碼裡需要訪問到的敏感資訊都寫在這裡,作為Swift常量。
struct AppSecrets {
static let secretKey="{{ argument.secretKey }}"
}
複製程式碼
- 新增 build phase 來生成 AppSecrets.swift。
Tools/Sourcery/bin/sourcery --sources Sources --templates Templates/AppSecrets.stencil --output Generated --args secretKey=\"$SECRET_KEY\"
複製程式碼
- Sourcery 的路徑取決於是怎麼安裝它的,我這例子裡是作為一個獨立版本工具,將它包含在專案中。
- 上面那個 sources 引數是必須的,雖然在這種情況下並沒有用到,只是在後面輸入了一個在專案裡的有效路徑。
- 上面 templates 引數是 AppSecrets.stencil 相對於 root 的路徑。
- output 資料夾是 AppSecrets.swift 寫入的地方,這個資料夾必須有。你可以在 build phase 新增
mkdir -p Generated
來確保這個資料夾存在。 - 寫這些引數的時候,他們分開的格式應該是這樣:
arg1=one,arg2=two
。我避免在 SECRET_KEY 內容裡新增任何特殊字元。
- 把 AppSecrets.swift 加入到 .gitignore 檔案中,讓它不被新增到 Git 倉庫中。
- 編譯專案,然後把 AppSecrets.swift 加到專案中,這樣 AppSecrets.swift 會被編譯和連結到專案的 target 中。
注意:如果你的 App key 之類的敏感資訊沒有要求在不同的編譯條件下( debug 和 release) 具有不同的值,你可以跳過 xcconfig 部分。你可以使用另一個方案,那就是把敏感資訊寫成一個可執行的 shell 指令碼,新增到 build phase 中。
總結一下,通過以上這些方法,你可以讓你專案中的敏感資訊不被加入到 Git 倉庫裡,與此同時,對於這個專案新的開發者們來說,也比較易於理解上手。