はじめに
iOSアプリエンジニアのあっきーです。今回は、タイトルに書いた通り、セキュリティ対応について書いていきます。ご挨拶がまだの方はこちらの記事からご覧ください。実際にリリースしたアプリはこちらです。興味があればぜひ触ってみてください。
個人で開発をしていると、「そこまでセキュリティを気にする必要あるかな?」と思う瞬間があります。ユーザ数もまだ少なく、当然ながら社内のセキュリティ担当者がいるわけでもありません。
とはいえ、インターネットに公開した瞬間から、APIやFirebaseのリソースは世界中からアクセス可能になります。そこで、個人開発だからこのくらいでいいかではなく、できる範囲では、やり過ぎなくらいやっておこうという方針でセキュリティ周りを実装しました。
Firebaseを使っているだけでは安心できない
今回のアプリでは、バックエンドにFirebaseを使っています。主に使用しているのはこちらです。
- Firebase Authentication
- Firestore
- Functions
- Storage
Firebaseを使えば認証やデータベースを簡単に実装できますが、当然ながら 「Firebaseを使っている = 安全」 というわけではありません。例えばCloud FunctionsのURLが分かれば、アプリを経由せず直接リクエストすることもできます。FirestoreやStorageも、Security Rulesの設定次第では意図しない読み書きを許可してしまいます。
そこで、まず導入したのがFirebase App Checkでした。
App Checkを導入
Firebase App Checkは、正規のアプリやデバイスからのリクエストかを検証するための仕組みです。App Checkを有効にすると、Firebase側で検証用トークンを確認できるようになります。今回のアプリでは、Cloud Functionsを含むFirebaseリソースに対してApp Checkを導入しました。
export const getPosts = onCall(
{
enforceAppCheck: true,
},
async (request) => {
// ...
正直、DeviceCheckやApp Attestの内部で行われている暗号処理まで理解したわけではありません。個人開発としては、自前で端末認証の仕組みを作るのではなく、AppleとFirebaseが提供している仕組みを利用して、不正アクセスのハードルを上げるという理解で導入しています。
Simulatorでの動作確認
SimulatorからFirebaseへアクセスすると、App Checkの検証に失敗します。Simulatorは実際のAppleデバイスではないため、本番用のattestation providerによる検証をそのまま通すことができません。開発環境では、Firebaseが用意しているDebug Providerを利用します。
#if targetEnvironment(simulator)
AppCheck.setAppCheckProviderFactory(AppCheckDebugProviderFactory())
#else
AppCheck.setAppCheckProviderFactory(CustomAppCheckProviderFactory())
#endif
Debug Tokenを登録する
Debug Providerを有効にしてアプリを起動すると、開発用のDebug Tokenを取得できます。このトークンをFirebase Consoleに登録することで、開発環境だけ特別扱いするイメージです。
App Checkだけで安心しない
今回のアプリでは、Cloud Functions側でも認証済みユーザであることをチェックしています。例えば、Function側では次のようにユーザ認証を確認しています。
if (!request.auth?.uid) {
throw new HttpsError(
"unauthenticated", "Authentication required."
);
}
App Check + Firebase Authenticationの両方を通すようにすることで、正規アプリかどうかに加えて、誰がアクセスしているのかのチェックをしています。
IAMも確認する
Firebaseを使っていると、Google Cloud側にさまざまなService AccountやIAM Roleが作成されます。最初は、 「Firebase Consoleだけ見ていればいいのでは?」 と思っていましたが、Cloud FunctionsやStorageなどを使い始めると、Google Cloud側の権限設定も関わってきます。
- どんなService Accountが存在するのか
- どんなRoleが付いているのか
- 必要以上に強い権限が付いていないか
- 不要なService Account Keyが存在しないか
個人開発では「自分しか触らないから強めの権限でもいいか」と考えがちですが、Service Accountまで必要以上の権限を持たせるのは別問題です。人間のアカウントだけではなく、アプリやサーバが使っている権限も確認しておく必要があります。基本的には「必要なものに、必要な権限だけを付ける」という最小権限を意識しました。
- 参考: Google Cloud IAM の概要
次回以降
今回は、App Checkを中心に「正規のアプリからのアクセスか」「誰がアクセスしているか」「サーバ側にはどんな権限があるか」を確認しました。ただし、ここまで対応してもFirestoreやStorage自体のアクセス制御が甘ければ意味がありません。次回は、Security Rulesや署名付きURLなど、実際のデータをどう守ったかについて書いていきます。
投稿予定の内容
- 個人開発だけどセキュリティは十分過ぎるほど対応したい 後編
- 個人開発アプリのサーバコストをできるだけ抑えたい
- 言葉の壁を越えたい ~iOSアプリに翻訳機能を組み込む~
- iOSアプリの投稿をWebでシェアする ~ディープリンクとOG画像~
- いろいろあってアプリの配信国を制限した
