Table of Contents
はじめに
Expo(React Native)アプリにFirebase App Checkを導入して、不正なアプリからのバックエンドへのリクエストを保護する手順をご紹介します。 App Checkの導入にはReact Native Firebase を使用します。
React Native Firebaseのインストール
すでにExpoアプリを作成してる前提で話を進めます。 公式にも手順があるので、詰まるところがあれば公式ドキュメントを参考にしてください。
まず最初にReact Native Firebaseをインストールします。
npx expo install @react-native-firebase/app
npx expo install @react-native-firebase/app-check
インストールするパッケージは2つで、コアの @react-native-firebase/app
とApp Check用の @react-native-firebase/app-check
をインストールします。
React Native Firebase設定
React Native Firebaseはネイティブ機能を使用するプラグインのため、Expo Goでは動かすことができません。
動かすには prebuild
コマンド を実行してAndroid/iOSでネイティブコードを出力して実行するか、Development Build を作成して動かす方法になります。
Prebuildをしてしまうとネイティブコードの管理をする必要になるため、基本的にはDevelopment Buildでの開発をお勧めします。
幸いReact Native FirebaseではExpoへの対応が考慮されていて、Config Plugin が提供されていてネイティブコードを触らずともExpoアプリへの導入が簡単にできます。
手順は以下を参考にしてください。
Config Pluginの設定はapp.jsonに追記するだけでOKです。
{
"expo": {
"android": {
"googleServicesFile": "./google-services.json",
"package": "com.mycorp.myapp"
},
"ios": {
"googleServicesFile": "./GoogleService-Info.plist",
"bundleIdentifier": "com.mycorp.myapp"
},
"plugins": [
"@react-native-firebase/app",
[
"expo-build-properties",
{
"ios": {
"useFrameworks": "static"
}
}
]
]
}
}
証明書プロバイダ設定手順
App Checkでは正規のアプリから送信されたリクエストか判定するために、証明書プロバイダを利用します。 AndroidはPlay Integrity/SafetyNet(非推奨)、iOSはDeviceCheck/App Attestという証明書プロバイダサービスに対応しています。
今回はAndroidでPlay Integrity、iOSでDeviceCheckを採用しました。
Android(Play Integrity)
まずはAndroid側のプロバイダを設定します。 サクッと簡単な流れだけ説明しますが、もっと詳しい手順は以下の記事が参考になりました。
対象アプリのGoogle Play Consoleへアクセスします。
App Integrity → Link Cloud project
Link existing project→使用しているFirebaseプロジェクトを選択
App signing → SHA256 certificate fingerprintをコピー
Firebaseコンソールへ移動 → App Check → 「アプリ」タブ → Play Integrity → コピーしたSHA256 certificate fingerprintをペースト、保存
iOS(DeviceCheck)
次にiOS側のプロパイダ設定になります。 こちらも以下の記事が参考になりました。
DeviceCheckを利用するにはPrivate Keyの作成が必要でした。 詳しい手順は公式でも提供されていますので、参考にしてください。
Apple DeveloperサイトでDeviceCheckを有効にしてPrivate Keyを作成します。
作成できたらAndroidと同じくFirebaseコンソールで認証キー、キーID、チームID(同じくApple Developerサイトから確認できます)を設定します。
これでAndroid/iOSとも証明書プロバイダの設定ができました。
デバッグトークンの作成
証明書プロバイダを設定して、Firebase側でApp Checkを有効にして不正なアプリからのリクエストをブロックできました。 しかしこのままだとPlay StoreやApp Storeからインストールしたアプリ以外は不正なアプリと認識されてしまい、開発時でもFirestoreやFunctionsへのアクセスが弾かれてしまいます。 それを防ぐためデバッグトークンというものを発行します。
Firebaseコンソール → App Check → iOS/Android各項目の右側メニュー(3ドットのアイコン)から「デバッグトークンを管理」をクリックしてデバッグトークンをiOS/Androidそれぞれ発行します。 それぞれ別の値にしても良いですが、この後説明する環境変数を1つに済ませるため同じ値を設定しておくと管理が楽になります。
Development Build作成
デバッグトークンでApp Checkが動いているか確認するために、Development Buildを作成して動かしてみます。
基本的に以下の記事と同じ流れになります。
eas.jsonの必要な部分だけ抜粋すると、
{
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"channel": "development",
},
},
}
となります。
ビルドするコマンドは
eas build --platform all --profile development
となります。
初期化処理
App Checkを動かすために初期化処理を記述する必要があります。 Expo Routerであれば_layout.tsxなどに書くことになると思います。
import { firebase } from "@react-native-firebase/app-check";
const appCheck = firebase.appCheck();
const rnfbProvider = appCheck.newReactNativeFirebaseAppCheckProvider();
rnfbProvider.configure({
android: {
provider: __DEV__ ? "debug" : "playIntegrity",
debugToken: process.env.EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN,
},
apple: {
provider: __DEV__ ? "debug" : "appAttestWithDeviceCheckFallback",
debugToken: process.env.EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN,
},
});
appCheck.initializeAppCheck({
provider: rnfbProvider,
isTokenAutoRefreshEnabled: true,
});
コードを少し解説すると、開発時はiOS/Androidともプロバイダを debug
に設定しています。
デバッグトークンは環境変数 EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN
から読み取るようにしています。(iOS/Androidとも同じ値のデバッグトークンを利用している前提のため環境変数が同じ)
最後に.env.localに環境変数を設定します。
EXPO_PUBLIC_FIREBASE_APP_CHECK_DEBUG_TOKEN=XXXX-XXXX-XXXX
Prefixに EXPO_PUBLIC_
をつけないとアプリ実行時に取得できないので気をつけてください。
これでApp Checkの導入が完了しました。 先ほどビルドしたDevelopment Buildアプリで動かしてみて、問題なくFirestoreなどへアクセスできたら成功です。
【おまけ】Firebaseリソース以外のリソースを保護する
App CheckではFirestoreやFunctionsなどのFirebaseリソースだけでなく、独自のバックエンドアプリでも不正なアプリからのリクエストを保護できます。
例えばNode.js(TypeScript)だとfirebase-adminを使って以下のように書くことができます。
import admin from 'firebase-admin';
import { initializeApp } from 'firebase-admin/app';
import { getAppCheck } from 'firebase-admin/app-check';
import serviceAccount from './service-account-key.json'; // Firebaseプロジェクトのサービスアカウントキーを指定
initializeApp({
credential: admin.credential.cert(serviceAccount),
});
async function check(token: string) {
try {
const appCheckClaims = await getAppCheck().verifyToken(token);
// 成功処理
} catch (err) {
// 失敗処理
}
}
Expo(React Native)側では以下のようにバックエンドへ送るトークンを取得できます。
try {
const { token } = await firebase.appCheck().getToken(true);
if (token.length > 0) {
console.log('AppCheck verification passed');
}
} catch (error) {
console.log('AppCheck verification failed');
}
おわりに
以上でExpoプロジェクトへのApp Check導入手順を紹介しました。 iOSアプリは良いですが、Androidアプリなどは簡単に改ざんしてapkアプリとして再配布可能ですので、セキュリティ対策として導入してみてはいかがでしょうか?