Top View


Author Daiki Urata

ExpoにFirebase App Checkを導入して不正なアプリからバックエンドを守る

2024/07/29

はじめに

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 App Integrity to project

Link existing project→使用しているFirebaseプロジェクトを選択

Link existing project

App signing → SHA256 certificate fingerprintをコピー

App signing key certificate

Firebaseコンソールへ移動 → App Check → 「アプリ」タブ → Play Integrity → コピーしたSHA256 certificate fingerprintをペースト、保存

Firebase Play Integrity Setup

iOS(DeviceCheck)

次にiOS側のプロパイダ設定になります。 こちらも以下の記事が参考になりました。

DeviceCheckを利用するにはPrivate Keyの作成が必要でした。 詳しい手順は公式でも提供されていますので、参考にしてください。

Apple DeveloperサイトでDeviceCheckを有効にしてPrivate Keyを作成します。

Create private key in Apple Developer site

作成できたらAndroidと同じくFirebaseコンソールで認証キー、キーID、チームID(同じくApple Developerサイトから確認できます)を設定します。

Firebase DeviceCheck setup

これでAndroid/iOSとも証明書プロバイダの設定ができました。

デバッグトークンの作成

証明書プロバイダを設定して、Firebase側でApp Checkを有効にして不正なアプリからのリクエストをブロックできました。 しかしこのままだとPlay StoreやApp Storeからインストールしたアプリ以外は不正なアプリと認識されてしまい、開発時でもFirestoreやFunctionsへのアクセスが弾かれてしまいます。 それを防ぐためデバッグトークンというものを発行します。

Firebaseコンソール → App Check → iOS/Android各項目の右側メニュー(3ドットのアイコン)から「デバッグトークンを管理」をクリックしてデバッグトークンをiOS/Androidそれぞれ発行します。 それぞれ別の値にしても良いですが、この後説明する環境変数を1つに済ませるため同じ値を設定しておくと管理が楽になります。

Create debug token for App Check

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アプリとして再配布可能ですので、セキュリティ対策として導入してみてはいかがでしょうか?

Daiki Urata

Daiki Urata

Twitter X

フロントエンド/モバイルアプリなどを主に開発しています。