Runbook: Mobile Security (Parky Flutter App)

最終更新: 2026-04-28 (Wave 3A / S-03 + S-04)

Parky モバイルアプリの「セキュリティ三層」(RASP / 生体認証 / 証明書ピンニング) の運用手順。

目的 実装ファイル 実装パッケージ
RASP ジェイルブレイク・改ざん検知 lib/services/security/jailbreak_check.dart freerasp
生体認証 重要アクション直前の step-up lib/services/security/biometric_gate.dart local_auth
証明書ピンニング MITM 防止 (Supabase / Parky API) lib/services/security/cert_pinning_service.dart http_certificate_pinning

1. freerasp (RASP) 本番化チェックリスト

1.1 Android signing cert hash の取得

android/app/build.gradle の release keystore を CI に配備した後、SHA-256 fingerprint を取得して jailbreak_check.dartsigningCertHashes を差し替える。

# 取得 (release.jks を持っているマシンで実行)
keytool -list -v -keystore release.jks -alias parky \
  | awk '/SHA256:/ { print $2 }'                      \
  | tr -d ':' | xxd -r -p | base64

差し替え対象:

// lib/services/security/jailbreak_check.dart
final androidConfig = AndroidConfig(
  packageName: 'co.parky.app',
  signingCertHashes: <String>[
    // 上記コマンドの出力をここに貼る
    'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==',
  ],
  ...
);

FIXME(2026-04-28) マーカーがあるので解消したらコメントごと削除する。

1.2 iOS Team ID の取得

Apple Developer Account → Membership → Team ID (10桁英数)。

final iosConfig = IOSConfig(
  bundleIds: <String>['co.parky.app'],
  teamId: 'XXXXXXXXXX', // ← Team ID
);

1.3 Android Gradle 要件

freerasp Android 実装は以下を要求する可能性が高い (実機ビルドで要確認):

  • compileSdkVersion >= 34
  • minSdkVersion >= 23
  • AGP 8 系で namespace 必須 (旧 flutter_jailbreak_detection の AGP 8 障害は freerasp で解決済)

ビルド失敗時は android/app/build.gradlecompileSdkVersion / minSdkVersion を確認。 ※ Windows 開発機では Flutter Android ビルドが通せないため、変更は Mac/Linux の CI 環境で検証する。

1.4 検知シグナルの Sentry tag

freerasp が tampering を検知すると、jailbreak_check.dart_reportThreat が Sentry に以下の tag 付きで captureMessage を送信する:

Tag 用途
component jailbreak_check コンポーネント絞り込み
device.tampered true tampering 全般のフィルタ
tamper.{signal} true 個別シグナル別の集計 (tamper.privilegedAccess 等)

監視対象 signal: privilegedAccess / debug / simulator / appIntegrity / hooks / secureHardware / systemVPN / passcode / deviceBinding / obfuscationIssues / devMode / unofficialStore

1.5 アプリブロック方針

Parky は freerasp で「アプリをブロック」しない。検知はリスク警告 + テレメトリ送信にとどめる。

TalsecConfig.killOnBypass は default の false のまま。誤検知で正規ユーザーが弾かれる ほうがビジネス影響が大きいため、検知 → 通報 → 運用判断のフローで運用する。

2. local_auth (生体認証) 本番化チェックリスト

2.1 iOS Info.plist

ios/Runner/Info.plist に以下を追加 (Mac で要編集、Windows 機からは触らない):

<key>NSFaceIDUsageDescription</key>
<string>サインアウトの確認等、重要な操作の本人確認に使用します。</string>

未追加の場合、FaceID 端末で LocalAuthentication.authenticate がクラッシュする。 TouchID は文言不要だが、FaceID/TouchID 共通の説明として上記を必ず入れる。

2.2 Android AndroidManifest.xml

android/app/src/main/AndroidManifest.xml<manifest> 直下に追加:

<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- API < 28 互換 (BiometricPrompt → FingerprintManager fallback) -->
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

2.3 適用箇所 (戦略)

全画面に貼ると UX が壊れる。リスクの高い strategic な箇所だけ。

現状の適用箇所:

画面 アクション 適用済
lib/screens/profile/settings_screen.dart サインアウト YES (Wave 3A)
lib/features/premium/... (TBD) IAP 購入確定 FIXME(2026-04-28) — IAP 画面確定後に配線
lib/features/profile/.../profile_edit_screen.dart メアド変更 FIXME(2026-04-28) — 変更検知後のみ要 step-up
退会 (deleted_at flag 設定) 退会確定 FIXME(2026-04-28) — 退会導線実装時に配線

2.4 失敗時の挙動

BiometricGate.requireForAction の戻り値:

  • success: アクション続行
  • failed: 中断 (UI 側で snackbar 等を出す判断は呼び出し側)
  • userCanceled: 中断 (静かに戻す)
  • unavailable: 端末未対応 → アクションを通す (誤動作で UX を壊さない)

3. cert pinning (証明書ピンニング) 運用

実装: lib/services/security/cert_pinning_service.dart

3.1 ピン取得コマンド

openssl s_client -connect <host>:443 -showcerts </dev/null 2>/dev/null \
  | openssl x509 -noout -pubkey \
  | openssl pkey -pubin -outform der \
  | openssl dgst -sha256 -binary \
  | base64

3.2 現行 pin set (2026-04-28)

Host Pin (SPKI SHA-256) 種別 Rotation
Supabase data plane kIdp6NNEd8wsugYyyIYFsi1ylMCED3hZbSR8ZFsa/A4= GTS WE1 intermediate ~3 年
Supabase root backup mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c= GTS Root R4 ~10 年
Supabase leaf GU2W4j1P24T3sqlI+o6YTnidzz0PI8fB/Gvd2ITfSZE= dev project leaf ~12 ヶ月
Parky API (CF Workers) kIdp6NNEd8wsugYyyIYFsi1ylMCED3hZbSR8ZFsa/A4= GTS WE1 intermediate ~3 年
Parky API (Xserver 旧) ngVpmshTjI2mGNZdkmE0BseYAQxwUyN7hAFExDPDwsg= CloudSecure RSA DV CA 2 FIXME(2026-04-28) prod CF 移行後に削除
Parky leaf MGpvY+qynViULn0WpbTi10bP+WLmW4pRfFuL6NUwWgw= dev-api leaf ~12 ヶ月

3.3 ローテーション手順

  1. 新証明書がデプロイされる に新フィンガープリントを取得
  2. _kSupabasePins / _kParkyPins に新旧 2 本を並列登録したアプリをリリース
  3. 旧証明書失効後、旧フィンガープリントだけを別 PR で削除 (移行期間で 2 リリース)

3.4 緊急時 bypass

CI / 統合テストで pin に阻害されるケース用に環境変数 bypass を用意:

flutter build apk --dart-define=CERT_PINNING_DISABLED=true

ただし production リリースで CERT_PINNING_DISABLED=true が混入することがないよう、 リリース CI workflow では渡さないこと。debug build では kDebugMode で常に bypass される。

4. リリース前チェックリスト

  • freerasp signingCertHashes を release keystore の SHA-256 で差し替えた
  • freerasp teamId を 10桁の Apple Team ID で差し替えた
  • freerasp bundleIds / packageName が release applicationId と一致
  • iOS Info.plistNSFaceIDUsageDescription を追加した
  • Android AndroidManifest.xmlUSE_BIOMETRIC / USE_FINGERPRINT を追加した
  • cert pin が release エンドポイント (api.parky.co.jp) のフィンガープリントを含む
  • Sentry に device.tampered=true の alert rule を追加 (week-over-week で異常検知)
  • BiometricGate を強化したい次の画面 (IAP / 退会 / メアド変更) を計画 doc 化
↗ Source markdown (runbook-mobile-security.md)