Flutterアプリのセキュリティチェックリスト
フェーズ0: ガバナンス、環境、CI/CD
CIゲートの強制
- 各PRで静的解析を実行:
flutter analyze、単体/UIテスト、dart pub outdated。 - SAST/SCA/DASTでHigh/Criticalの検出があった場合はリリースをブロック。リリースブランチではLinterエラーゼロを義務付ける。
シークレットの処理
- Dartソースコード、
pubspec.yaml、ネイティブコード、またはログにシークレットがないこと。 - ビルド時の設定注入には環境変数(例:
--dart-define)を使用する。 - アナリティクス(analytics)のシークレットを編集(マスク)する。
- キー/トークンを定期的に、および漏洩が疑われる場合にローテーションする。
エビデンスとトレーサビリティ
- スキャンレポート、SBOM、ビルドログ、およびリリースノートを各アーティファクトとともにアーカイブする。
Scanning automation
- モバイル用SAST/DAST/SCAプラットフォーム(Flutter対応)をCI/CDに統合し、各PRおよびプレリリースで実行し、High/Criticalの問題で自動的にゲートをフェイルさせる。
フェーズ1: 脅威モデリング、データ分類、およびインベントリ
- データ(PII、認証情報、トークン、支払い)を分類し、収集、処理、保存(ファイル、安全なDB)、送信をマッピングする。
- 信頼境界(trust boundaries)を特定する(Dart環境/Isolate、MethodChannel、ネイティブライブラリ、プラグイン、バックエンドAPI)。
- サードパーティの
pub.devパッケージのインベントリを作成。適切に保守・検証された公開者(verified publishers)であり、最小権限のプラグインを優先する。 - Dartパッケージの攻撃面を最小限に抑える。
Phase 2: Build and configuration hardening
Release settings
- すべての本番ビルドがAOTを有効にしたリリースモード(
flutter build apk/ios --release)でコンパイルされていることを確認する。 - 本番ビルドから開発用エンドポイントと機能フラグ(feature flags)を削除する。
Android specific
- Gradleで
minifyEnabled trueおよびshrinkResources trueを使用する。 -
AndroidManifest.xmlに必要な権限のみを宣言する。 -
networkSecurityConfigで平文トラフィックを無効にする。
iOS specific
- デバッグシンボルとデッドコードを削除(ストリップ)する。
- 必要なエンタイトルメントのみを付与する。
-
Info.plistでNSAllowsArbitraryLoads = falseを設定する。
Phase 3: Local storage and secure storage
セキュアストレージ
- すべてのトークン、機密性の高いPII、および暗号化キーには
flutter_secure_storageプラグイン(または同等のもの)を使用する。 - Android:プラグインがKeystoreでバックアップされた暗号化(EncryptedSharedPreferences)を使用していることを確認する。
- iOS:プラグインが適切なアクセス可能性設定(例:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly)でKeychainを使用していることを確認する。
Files and databases
- シークレットや機密性の高いPIIを平文で
SharedPreferencesやローカルファイルに保存しない。 - 機密データを保存する場合は、データベース(SQLCipher付きのsqfliteなど)を暗号化する。
- シークレットをログに(一時的であっても)書き込まない。
Caches and derived data
- PIIをキャッシュ/一時ディレクトリに永続化しない。
- ログアウト時にキャッシュ/一時ディレクトリをパージする。
Phase 4: Privacy safeguards (UI, clipboard, screenshots)
- アプリスイッチャーで機密UIを非表示にする(例:
secure_applicationプラグインを使用するか、画面をぼかす/隠すネイティブコードを使用)。 - アプリ起動時のクリップボードの読み取りを避ける。
- パスワード、キー、長寿命のトークンをシステムクリップボードに絶対に入れない。
Phase 5: Network security and TLS/pinning
HTTPS/TLSの強制
- すべてのアプリ通信にHTTPSを強制する。DartのデフォルトのHTTPクライアントはTLSを強制しますが、不正な証明書を拒否するように設定する必要があります。
- 本番環境で証明書チェックを絶対にバイパスしない(
badCertificateCallback)。
証明書ピンニング
- SSL/TLS証明書のピニングを実装する(例:
SecurityContextオブジェクトでSHA-256をチェックすることによって)。 - MITMのシナリオおよび期限切れ/ローテーションされたリーフ(leaf)証明書に対する耐性を検証する。
- 安全なピンローテーションとフォールバック(バックアップ)を計画する。
Cookies and tokens
- デバイス上の長寿命のベアラートークン(bearer tokens)を避ける。
フェーズ6: 認証、認可、セッション
認証フロー
- パブリック認証フローにはPKCEを伴うOAuth2/OIDCを使用する(例:
flutter_appauthを使用)。 - クライアントシークレットや認証関連のAPIキーをDartのソースコードに絶対に埋め込まない。
- 認証には、アプリ内のWebViewよりもシステムブラウザコンポーネント(Chrome Custom Tabs、SafariViewController)を優先する。
トークン
- 短寿命のアクセストークン(access tokens)を使用する。
- リフレッシュトークン(refresh tokens)を安全に保存する(例:
flutter_secure_storage)。 - ログアウト、デバイス変更、不審なアクティビティの際にトークンをローテーションおよび無効化する。
認可 (Authorization)
- サーバー側の認可(authorization)を強制する。Flutterアプリの状態は信頼できない(untrusted)ものとして扱う。
Phase 7: Inter‑app communication and deep links
App Links / Universal Links
- 可能な場合は、カスタムURLスキームの代わりにAndroid App LinksとiOS Universal Linksを使用する。
- すべての受信リンクの送信元とパラメーターをDartコード内で検証する。
Custom schemes and intents
- カスタムスキームを使用する場合は、すべての受信データとデータ型の堅牢な検証を確実にする。
- URLハイジャックとオープンリダイレクトを防御する。
Phase 8: Reverse engineering and tamper resilience
Code obfuscation
- FlutterのリリースモードでDartがネイティブAOTコード(Android/iOS)にコンパイルされていることを確認する。
- ビジネスロジックとクラス名を隠すために、Flutterの難読化サポート(
flutter build apk --obfuscate --split-debug-info=/<dir>)を使用する。
環境チェック
- 信頼できるプラグイン(
flutter_jailbreak_detection、freeraspなど)を使用して脱獄(iOS)とルート化(Android)を検出する。 - 疑わしい環境に対して比例的(機能制限、警告、または終了)に対応する。
Anti-tamper
- バックエンドでの構成証明(attestation)のために、ネイティブの完全性チェック(Androidの場合はPlay Integrity API、iOSの場合はDeviceCheck/App Attest)に依存する。
- クライアント側のチェックはシグナルとして扱い、唯一のセキュリティコントロールとして扱わない。
Phase 9: Cryptography and randomness
- 精査されたDartの暗号化実装(例:
cryptoやpointycastleパッケージ)を使用するか、MethodChannelを介してネイティブプラットフォームのプリミティブに任せる。 - Dartの暗号論的に安全なRNG(
Random.secure())を使用する。 - 対称暗号化には最新のモード(AES-GCMなど)を使用する。
- IVまたはハードコードされた暗号化キーを絶対に再利用しない。
Phase 10: Permissions, notifications, and identifiers
Privacy‑related permissions
- アプリのコンテキストの明確な説明を伴って、使用時(point-of-use)に権限を要求する。
- デバイスのセンサー(位置情報、カメラ、マイク)へのアクセスを厳密な必要性に制限する。
Identifiers
- デバイスのハードウェアIDのバインディングを避ける。アプリ固有のリセット可能な識別子を使用する。
通知
- プッシュ通知のペイロード内の機密データを最小限に抑える。開いたときのフェッチ(fetch-on-open)に依存する。
Phase 11: Logging, analytics, and crash reporting
ロギングの衛生管理
- リリースビルドでデバッグログとDartの
print()を無効にする(Flutterのfoundation APIのkReleaseMode/kDebugModeを使用)。 - アナリティクスイベントとクラッシュレポート(Firebase Crashlytics、Sentryなど)からPIIとシークレットを編集(マスク)する。
- 難読化マッピングファイル(
--split-debug-info)がクラッシュの難読化解除のためにサーバー側に安全に保管され、アプリと一緒に出荷されないことを確認する。
Phase 12: Testing, DAST, and runtime verification
Interception tests
- アプリのMITM攻撃に対する耐性を検証する(特別な信頼設定なしでプロキシが傍受した場合に、Dartのネットワーク接続が失敗することを確認する)。
API悪用テスト
- FlutterアプリをサポートするバックエンドAPIに対して、レート制限の欠如、大量割り当て(mass assignment)、および認可の脆弱性(IDOR/BOLA)をテストする。
Dynamic analysis
- モバイルDAST(DartのHTTPクライアントトラフィックを検査する)を実行して、構成の誤ったAPIとデータ漏洩を特定する。
- トリアージおよび再テストのためのDASTの証拠をエクスポートする。
Phase 13: SBOM, and evidence
SBOMと依存関係
- Dart依存関係とネイティブコンポーネント(
pubspec.yaml、build.gradle、Podfile)の脆弱性を監視する。 - Flutter/DartパッケージのSBOMを生成する。
エビデンスバンドル
- 分析(SAST/DAST)アーティファクトをリリース記録に添付する。
- SBOMをリリース記録に添付する。
実務者向けクイックリファレンス
- Config: AOTリリース。ログ/エンドポイントを隠すために
kReleaseModeを使用。--obfuscateをオンにする。 - Data: 常に
flutter_secure_storageまたはネイティブの暗号化ブリッジを使用。prefs/SQfliteに平文のPIIを入れない。 - Network: 常にHTTPS。
SecurityContextを介して証明書をピニング。本番環境でバイパスするためのbadCertificateCallbackを使わない。 - Auth: OAuth(
flutter_appauth)にはシステムブラウザ + PKCEを使用。ハードコードされたシークレットなし。 - Tamper: ルート/脱獄の検出(freeraspなど)、バックエンドの構成証明(Play Integrity / DeviceCheck)。
- Privacy: バックグラウンドのスクリーンショットをぼかす。クリップボードデータをクリア。JITで権限を要求する。
- Dart/Flutter: 認可の決定においてフロントエンドを信頼しない(API must verify)。承認された制限付きの
pub.devパッケージに依存する。