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.dart の signingCertHashes を差し替える。
# 取得 (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 >= 34minSdkVersion >= 23- AGP 8 系で namespace 必須 (旧
flutter_jailbreak_detectionの AGP 8 障害は freerasp で解決済)
ビルド失敗時は android/app/build.gradle の compileSdkVersion / 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 ローテーション手順
- 新証明書がデプロイされる 前 に新フィンガープリントを取得
_kSupabasePins/_kParkyPinsに新旧 2 本を並列登録したアプリをリリース- 旧証明書失効後、旧フィンガープリントだけを別 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.plistにNSFaceIDUsageDescriptionを追加した - Android
AndroidManifest.xmlにUSE_BIOMETRIC/USE_FINGERPRINTを追加した - cert pin が release エンドポイント (api.parky.co.jp) のフィンガープリントを含む
- Sentry に
device.tampered=trueの alert rule を追加 (week-over-week で異常検知) - BiometricGate を強化したい次の画面 (IAP / 退会 / メアド変更) を計画 doc 化