コンテンツにスキップ

Androidアプリのセキュリティチェックリスト

フェーズ0:ガバナンス、環境、およびCI/CD

CIゲートの適用(CI gates)

  • 各PRで静的チェックを実行:ktlint / Android Lint、ユニット/UIテスト、依存関係監査(Gradle、SBOM)。
  • SAST/SCA/DASTのHigh/Criticalな発見事項についてリリースをブロック。リリースブランチでlintエラーがゼロであること。

シークレットの取り扱い

  • ソース、BuildConfig、AndroidManifest.xml、リソース、またはログにシークレットを含めない。
  • 環境/シークレットマネージャー(例:Gradleシークレット、クラウドKMS)を使用。アナリティクス内のシークレットを墨塗り(redact)。
  • キー/トークンを定期的に、また漏洩の疑いがある場合はローテーションする。

証拠とトレーサビリティ

  • スキャンレポート、SBOM、ビルドログ、およびリリースノートを各アーティファクトとともにアーカイブする。

スキャンの自動化

  • モバイルSAST/DAST/SCAプラットフォームをCI/CDに統合し、各PRおよびプレリリースで実行し、High/Criticalな問題が発生した場合はゲートを自動的に失敗させる。

フェーズ1:脅威モデリング(Threat modeling)、データ分類、およびインベントリ

  • データ(PII、認証情報、トークン、支払い)を分類し、収集、処理、保存(SharedPreferences、ファイル、データベース、Keystore)、および送信をマッピングする。
  • 信頼境界(デバイス、アプリのサンドボックス、Keystore、IPC/Binder、WebView、ネイティブライブラリ、バックエンドAPI)を特定する。
  • サードパーティSDKのインベントリを作成。メンテナンスが行き届き、署名された、最小特権のSDKを優先し、数を最小限に抑える。

フェーズ2:ビルドと構成の堅牢化(Gradle + Manifest)

Gradle/Release設定(Release settings)

  • コードの縮小/難読化機能を持つリリースビルドを使用する(minifyEnabled trueshrinkResources true)。
  • 出荷されるAPK/AABからデバッグシンボルを取り除く。
  • マッピングファイルはサーバー側に保持する。
  • テスト/デバッグ用ライブラリをリリースから除外する。
  • 開発用エンドポイントとフィーチャーフラグ(feature flags)を本番ビルドから削除する。

署名と権限

  • 署名キーを保護する(ハードウェアバックのキーストア、アクセス制限)。
  • 専用のリリース署名構成を使用する。
  • AndroidManifest.xml には必要な権限のみを宣言する。
  • 過度な危険な権限(dangerous permissions)を避ける。

ネットワークセキュリティ構成

  • networkSecurityConfig を使用してTLSを強制する。
  • クリアテキストトラフィックを許可しない(android:usesCleartextTraffic="false")。
  • ドメイン構成の上書きは、必要な場合にのみ期限付きで使用する。

マニフェストの衛生管理

  • 厳密に必要でない限り、コンポーネントに exported="true" を設定しない。エクスポートする場合は権限で制限する。
  • デバッグフラグを削除する(リリースでは android:debuggable="false"、適切な暗号化を伴う場合を除き allowBackup="true" を設定しない)。
  • すべての実行時権限(runtime permissions)について、正確な権限の根拠テキスト(アプリ内)を提供する。

フェーズ3:ローカルストレージとKeystoreのセキュリティ

Keystoreの使用

  • 長期キーは、実行可能な最も強力な保護(利用可能な場合はハードウェアバック、UXが許せばユーザー認証)を備えたAndroid Keystoreに保存する。
  • 非常に機密性の高いキーには setUserAuthenticationRequired(true) を使用し、必要に応じて生体認証/ロック画面で操作を制限する。
  • ログアウト時またはデバイス変更時に、Keystoreでラップされたシークレットを削除する。
  • 高リスクなトークンの場合、デバイスバインディング(device-binding)を検討する。

ファイル、SharedPreferences、データベース

  • プレーンテキストファイルまたは SharedPreferences にシークレットや機密PIIを保存しない。
  • 機密データベース/ファイルを暗号化する(例:SQLCipherまたは同等のファイル暗号化)。
  • アプリ専用ストレージ(MODE_PRIVATE)を使用する。
  • サポートされている場合はファイル保護フラグを適用する。
  • バックアップに暗号化されていないシークレットが含まれていないことを確認する。

キャッシュと派生データ

  • キャッシュ/一時ファイルにPIIを永続化しない。
  • 必要に応じて、ログアウト時およびバックグラウンド処理の一環として、キャッシュ/一時ファイルをパージする。
  • 一時的であっても、シークレットをログや SharedPreferences に書き込まない。

フェーズ4:プライバシーの保護(UI、クリップボード、スクリーンショット)

  • アプリスイッチャーのスクリーンショットで機密UIを墨塗りする(例:機密のActivities/Viewsで FLAG_SECURE を使用する)。
  • 非常に機密性の高いフローについては、追加のマスキングまたは別の「セキュアモード」のActivitiesを検討する。
  • アプリの起動時にクリップボードを読み取らない。パスワード/長期トークンをクリップボードに配置しない。

フェーズ5:ネットワークセキュリティとTLS/ピニング(pinning)

HTTPS/TLSの適用

  • 信頼できるホストにHTTPSを強制する。無効な証明書/ホスト名を拒否する。HTTPへのフォールバックを無効にする。
  • 厳格なタイムアウト、ジッター(jitter)を伴うエクスポネンシャルバックオフを設定し、TLSエラー時にはフェイルクローズ(fail closed)する。

証明書のピニング(Certificate pinning)

  • SPKI SHA-256ピニングを優先。ホストごとに少なくとも1つのバックアップピンを含める。
  • OkHttp / カスタム TrustManager または networkSecurityConfig を介してピニングを実装する。
  • MITMシナリオおよび期限切れ/ローテーションされたリーフ証明書に対して検証する。
  • 安全なピンのローテーションを計画する。ピンの失敗に関するテレメトリを含める。
  • Webフローには、セキュア、HttpOnly、SameSiteのCookieを使用する。
  • デバイス上の長期ベアラートークン(bearer tokens)を避ける。
  • 機密セッション後にCookieとWebViewデータをクリアする。

フェーズ6:認証、認可、およびセッション

認証フロー

  • パブリッククライアントにはPKCEを使用したOAuth2/OIDCを使用する。アプリにクライアントシークレットを絶対に埋め込まない。
  • ログインには、アプリ内のWebViewよりもシステム/ブラウザベースの認証(Chrome Custom Tabs / デフォルトブラウザ)を優先する。

トークン

  • 短期のアクセストークン(access tokens)を使用する。
  • リフレッシュトークン(refresh tokens)は、Keystoreキーにバインドされた暗号化ストレージにのみ保存する。
  • ログアウト時、デバイス変更時、および不審なアクティビティ発生時に、トークンをローテーションして無効化する。
  • 必要に応じて、セッションをデバイスのコンテキストリスク(device risk)にバインドする。

認可

  • サーバー側で認可を強制する。クライアント側のチェックはアドバイザリ(参考)としてのみ扱う。
  • アプリが使用するすべてのバックエンドAPIにわたって、IDOR/BOLAの脆弱性と権限昇格をテストし、防止する。

フェーズ7:WebViewの堅牢化

  • 必要な場合にのみWebViewを使用する。可能な場合は認証と支払いにブラウザを優先する。

デフォルト

  • 厳密に必要でない限りJavaScriptを無効にする。
  • JavaScriptを有効にする場合は、機能を制限し、setAllowFileAccessFromFileURLs および setAllowUniversalAccessFromFileURLs を無効にする。
  • 特にAPI < 17では、機密メソッドを公開するオブジェクトで addJavascriptInterface を有効にしない。
  • ナビゲーションの許可リスト(ホスト/パス)を強制する。file://javascript:、および信頼できないスキームをブロックする。
  • 可能な場合は、機密セッションに非永続的なWebViewストレージを使用する。
  • ログアウト時にCookie、キャッシュ、および履歴をクリアする。

コンテンツセキュリティ

  • ファーストパーティのWebコンテンツはHTTPSのみで提供する。
  • 混在コンテンツを許可しない(MIXED_CONTENT_NEVER_ALLOW)。
  • リモートコンテンツの整合性がチェックされ、制御されていることを確認する(厳密に必要でない限り、信頼できないサードパーティのスクリプトを使用しない)。

  • ディープリンクにはApp Links(検証済みリンク)を優先する。受信したリンクのオリジンとパラメータを検証する。

カスタムスキームとインテント(intents)

  • カスタムスキームまたは暗黙的インテントが使用される場合は、すべての受信データの一意性と堅牢な検証を確保する。
  • エクスポートされたActivities/Services/BroadcastReceiversを権限または明示的インテントで保護する。過度に広範なインテントフィルターを避ける。
  • 呼び出し元のIDとパラメータをチェックすることにより、インテントスプーフィング(intent spoofing)、URLハイジャック、およびオープンリダイレクトから防御する。

フェーズ9:リバースエンジニアリングと改ざんに対する耐性(tampering)

静的漏洩の低減

  • リリースビルドから、デバッグビルド、詳細ログ、デバッグメニュー、およびテスト用エンドポイントを削除する。
  • コード(ProGuard/R8)とリソースを難読化し、縮小する。
  • 意味のある文字列(APIキー、シークレット)をリソースやネイティブライブラリに残さない。

環境チェック

  • root/フッキング/デバッガーのインジケーター(一般的なrootパス、書き込み可能なシステムディレクトリ、インスツルメンテーションフレームワーク)を検出し、シグナルを記録する。
  • UXを維持しながら、リスクに基づいて比例して対応する(機能の低下、追加の検証、またはブロック)。

改ざんテスト(Tamper testing)

  • 再パッケージ化または再署名されたアプリが整合性チェックまたはサーバーアテステーション(例:セキュリティ/アテステーションAPI)に失敗することを確認する。
  • クライアント側のシグナルをバックエンドへのヒントとして扱う。単独のセキュリティコントロールとして決して扱わない。

フェーズ10:暗号化とランダム性

  • プラットフォームが提供する暗号プリミティブ(Java Cryptography Architecture、Android Keystore)を使用する。カスタムアルゴリズムを構築しない。
  • 認証付き暗号化には、AES-GCM/ChaCha20-Poly1305などの最新のAEADモードを使用する。
  • 適切なパラメータとソルトとともに確立されたKDF(PBKDF2/HKDF)を使用する。
  • セキュアなRNG(SecureRandom またはプラットフォームAPI)を使用してキー/IVを生成する。
  • IV/ノンスを決して再利用しない。
  • キーはKeystoreに保持するか、保存時に暗号化する。

フェーズ11:権限、通知、および識別子

  • 使用時点で必要最小限の権限を要求し、ユーザーにとっての価値をコンテキストに合わせて明確に説明する。

プライバシー関連の権限

  • 位置情報、連絡先、カメラ、マイク、ストレージ、電話の状態へのアクセスを必要最小限に制限する。
  • 拒否および取り消しをグレースフルに(gracefully)処理する。
  • ユーザーにプレッシャーをかけるダークパターン(dark patterns)を避ける。

識別子とトラッキング

  • アプリスコープの、またはリセット可能な識別子を優先する。アプリ間で永続的なフィンガープリンティング(fingerprinting)を構築しない。
  • プラットフォームのポリシーに従ってのみ、広告IDまたはアナリティクスIDを使用する。
  • トラッキングに禁止されているハードウェア識別子を決して使用しない。

通知

  • 通知コンテンツ内の機密データを最小限に抑える。
  • 可能な場合は、一般的な表現を使用し、ロック画面では詳細を非表示にする。

フェーズ12:ロギング、アナリティクス、およびクラッシュレポート

  • 重大度レベルを持つ構造化ロギングを使用する。リリースビルドでは詳細/デバッグロギングを無効にする。
  • ログ、アナリティクスイベント、およびクラッシュレポートからPIIとシークレットを墨塗りする。
  • 可能な場合はアナリティクスを集約してサンプリングする。
  • クラッシュの難読化解除のために、難読化されたビルドのシンボル/マッピングファイルがサーバー側に安全に保存されていることを確認する。
  • アプリ内にシンボル/マッピングファイルを出荷しない。

フェーズ13:テスト、DAST、および実行時検証

トラフィック傍受テスト

  • MITMに対する耐性(無効な証明書、ホスト名の不一致、信頼できないCA)を検証する。
  • 構成されている場合、ピニングが傍受をブロックすることを検証する。

実行時検査

  • ログにシークレット/PIIが表示されないことを確認する。
  • 機密データが保存時に暗号化されていることを検証する。
  • 実行時のWebViewの動作(ナビゲーション、JS、ストレージ)を確認する。
  • 実行時のローカルストレージの動作(ファイル、SharedPreferences、データベース)を確認する。
  • Keystoreバックのキーが期待される場所で使用されていることを検証する。
  • Keystoreキーのアクセス制御が設計通りに機能していることを検証する。

API悪用テスト

  • レート制限(rate limiting)をテストする。
  • リプレイ保護(replay protection)をテストする。
  • ページネーションの境界をテストする。
  • 大量割り当て(mass assignment)をテストする。
  • バックエンドAPIのエラーメッセージの衛生管理をテストする。

動的解析

  • コアフローを実行しながらモバイルDASTを実行し、トランスポートの構成ミス、WebViewの問題、ストレージの漏洩、およびロギングの問題を明らかにする。
  • トリアージと再テストのためにDASTから証拠をエクスポートする。

フェーズ14:SBOMおよび証拠

SBOMと依存関係

  • すべてのGradleモジュールおよび組み込みSDKのSBOMを生成する。
  • CVEをトラッキングし、依存関係を定期的に更新する。
  • リリース準備の一環として依存関係の健全性を監視する。
  • リリース準備の一環としてライセンスのコンプライアンスを監視する。

証拠のバンドル

  • スキャンアーティファクトをリリースレコードに添付する。
  • SBOMをリリースレコードに添付する。
  • マニフェストの差分をリリースレコードに添付する。
  • 権限とコンポーネントのエクスポートリストをリリースレコードに添付する。
  • 自動/手動テストレポートをリリースレコードに添付する。

フェーズ15:レポートと基準の整合性

レポート

  • エグゼクティブサマリー、範囲/方法論、PoC/証拠を含む詳細な発見事項、リスク評価、修復ガイダンス、および再テストの結果を提供する。

基準マッピング(例)

  • ARCH:フェーズ1–2
  • PLATFORM:フェーズ2、7–8、11
  • STORAGE:フェーズ3–4
  • CRYPTO:フェーズ10
  • AUTH:フェーズ6
  • NETWORK:フェーズ5
  • CODE:フェーズ2、9、12
  • RESILIENCE:フェーズ9

実務担当者向けクイックリファレンス

  • Manifest/Network:リリース時にクリアテキストトラフィックがないこと。厳格な networkSecurityConfig ドメイン。強力な正当性なしにコンポーネントをエクスポートしないこと。
  • シークレットとストレージ:長期キーにはAndroid Keystoreを使用。DB/ファイルを暗号化。シークレットを SharedPreferences やログに絶対に保存しないこと。
  • Network:HTTPSのみ。クリティカルなエンドポイントのバックアップを備えた証明書/SPKIピニング。MITMの試みが失敗することを検証すること。
  • Auth:OIDC + PKCE。短期アクセストークン。暗号化ストレージ内のリフレッシュトークン。サーバー側での認可。IDOR/BOLAのテスト。
  • WebView:認証にはブラウザを優先。WebViewの場合、デフォルトでJSをオフ、インターフェースの制限、ナビゲーションの許可リスト、機密フロー用の非永続的ストア。
  • プライバシー:機密画面には FLAG_SECURE を使用。クリップボードのシークレットを避ける。権限を最小限に抑え、明確な説明を提供すること。
  • リリースの衛生管理:難読化と縮小。デバッグログ/メニュー/テストエンドポイントの削除。各リリースで権限とエクスポートされたコンポーネントを検証すること。