コンテンツにスキップ

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

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

CIゲートの適用(CI gates)

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

シークレットの取り扱い

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

証拠とトレーサビリティ

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

スキャンの自動化

  • モバイルSAST/DAST/SCAプラットフォームをCI/CDに統合し、各PRおよびプレリリースで実行する(例:Ostorlab)。
  • SAST/DAST/SCAからのHigh/Criticalな問題でCIゲートを自動的に失敗させる。

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

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

フェーズ2:ビルドと構成の堅牢化(Xcode + Info.plist)

XcodeのRelease設定(Release settings)

  • シンボルとデッドコード(dead code)を取り除く。
  • モジュール全体の最適化(whole-module optimization)を有効にする。
  • リリースビルドでDEBUGフラグを無効にする。
  • テスト/デバッグ用フレームワークをリリースから除外する。
  • 開発用エンドポイントとフィーチャーフラグ(feature flags)をリリースから削除する。

コード署名とエンタイトルメント(entitlements)

  • 必要なエンタイトルメントのみを付与する(使用されていないApp Groups、Keychain Sharing、NFC、Bluetoothなどは不可)。
  • 専用のリリースプロファイルを使用した自動署名を利用する。
  • 署名用クレデンシャルを保護する(例:CIのシークレットストア、アクセス制御)。

App Transport Security(ATS)

  • NSAllowsArbitraryLoads = false に設定する。
  • TLS 1.2+のみを優先する。
  • 厳格な NSExceptionDomains は、必要な場合にのみ期限付きで使用する。

Info.plistの衛生管理

  • 必要な場合を除き、ドキュメントの共有を無効にする:
  • UIFileSharingEnabled = false
  • LSSupportsOpeningDocumentsInPlace = false
  • LSApplicationQueriesSchemes を最小限に抑える。カスタムスキームよりもUniversal Linksを優先する。
  • すべての権限について、正確な NS[AppData/AppleEvents/SystemAdministration]UsageDescription 文字列を提供する。

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

Keychainの使用

  • トークン/シークレットは、可能な限り最も強力なクラスでKeychainに保存する:
  • kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  • または、適用可能な場合は kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
  • UXが許せば、kSecAccessControl フラグ(例:biometryCurrentSetuserPresence)を使用して機密アイテムをゲート制御する。
  • ログアウト時にKeychainアイテムを削除する。
  • 高リスクなトークンの場合、デバイスバインディング(device-binding)を検討する。

ファイル保護とデータベース

  • ファイルにデータ保護を適用する:NSFileProtectionComplete(デフォルト属性またはファイル単位)。
  • 機密データベース/ファイルを暗号化する(例:SQLCipherまたは同等のもの)。
  • シークレットをプレーンテキストで決して保存しない。

キャッシュと派生データ

  • キャッシュ/一時ファイルにPIIを永続化しない。
  • 必要に応じて、ログアウト時に機密なキャッシュ/一時データをパージする。
  • 必要に応じて、バックグラウンド処理で機密なキャッシュ/一時データをパージする。
  • UserDefaults にシークレットを書き込まない。

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

  • バックグラウンドに移行する際に機密UIを墨塗りする(sceneWillResignActive / applicationWillResignActive でのぼかしまたはプレースホルダー)。
  • 画面キャプチャ(UIScreen.main.isCaptured)を検出し、非常に機密性の高いビューについては動作を調整する。
  • アプリの起動時にクリップボードを読み取らない。
  • UIPasteboard.general にシークレットを決して配置しない。

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

URLSession/ATS

  • 信頼できるホストにHTTPSを強制する。
  • 無効な証明書とホスト名を拒否する。
  • HTTPへのフォールバックを無効にする。
  • 厳格なタイムアウトを設定する。
  • 該当する場合は、ジッター(jitter)を伴うリトライを実装する。
  • TLSエラー時にはフェイルクローズ(fail closed)する。
  • サーバーのTLS設定でTLS 1.2+の使用を強制する。

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

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

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

認証フロー

  • パブリッククライアントにはPKCEを使用したOAuth2/OIDCを使用する。
  • アプリにクライアントシークレットを絶対に埋め込まない。
  • 認証には、WebViewよりもシステムブラウザ(ASWebAuthenticationSession / SafariViewController)を優先する。

トークン

  • 短期のアクセストークン(access tokens)を使用する。
  • リフレッシュトークン(refresh tokens)はKeychainに保存する。
  • ログアウト時にトークンをローテーションして無効化する。
  • デバイス変更時にトークンをローテーションして無効化する。
  • 必要に応じて、セッションをデバイスのコンテキストリスク(device risk)にバインドする。
  • 異常(geo/IP/ASNの変更)を検出し、対応する。

認可

  • サーバー側で認可を強制する。クライアント側のチェックはアドバイザリ(参考)として扱う。
  • IDOR/BOLAの脆弱性を検証する。
  • 権限昇格の問題を検証する。

フェーズ7:WebViewの堅牢化(WKWebView)

  • 必要な場合にのみ WKWebView を使用する。
  • 分離のためにアプリバウンドドメイン(WKAppBoundDomains)を優先する。

デフォルト

  • 必要な場合を除きJavaScriptを無効にする。
  • javaScriptCanOpenWindowsAutomatically を無効にする。
  • WKNavigationDelegate を介してナビゲーションを許可リスト化する。
  • file:// URLをブロックする。
  • javascript: URLをブロックする。
  • 機密セッションには非永続的なデータストアを使用する。
  • ログアウト時に HTTPCookieStore とWebサイトデータをクリアする。
  • リリース時にWeb Inspectorが無効になっていることを確認する(iOS 16+ isInspectable = false)。

コンテンツセキュリティ

  • ファーストパーティのWebコンテンツには、CSPを強制する。
  • HTTPSを強制する。
  • 混在コンテンツを許可しない。

  • Universal Linksを優先する。
  • 受信したリンクのオリジンとパラメータを検証する。

カスタムURLスキームが使用される場合

  • 一意のスキームを選択する。
  • すべてのパラメータを検証する。
  • URLハイジャックから防御する。
  • オープンリダイレクトから防御する。
  • LSApplicationQueriesSchemes を既知の必要なスキームに限定する。

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

静的漏洩の低減

  • ストアに出荷されるバイナリからシンボルを取り除く。
  • クラッシュシンボリケーションのためにdSYMをサーバー側に保存する。
  • リリースビルドから詳細ログを削除する。
  • リリースビルドからデバッグメニューを削除する。
  • リリースビルドからテスト用エンドポイントを削除する。
  • 本番向けではないフィーチャートグル(feature toggles)を削除する。

環境チェック

  • ジェイルブレイクの存在(ファイル、システムコール、不審なライブラリ)に関するシグナルを記録する。
  • フッキングフレームワークに関するシグナルを記録する。
  • デバッガーの存在に関するシグナルを記録する。
  • クライアントのチェックのみに依存せず、比例的に対応する(機能低下/ブロック/テレメトリ)。

改ざんテスト(Tamper testing)

  • 再パッケージ化されたビルドが整合性チェックまたはサーバーアテステーションに失敗することを検証する。
  • 再署名されたビルドが整合性チェックまたはサーバーアテステーションに失敗することを検証する。
  • クライアント側のチェックはシグナルとして扱い、単独のセキュリティコントロールとしては決して扱わない。

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

  • プラットフォームの暗号化(CryptoKit / CommonCrypto)を使用する。カスタムの暗号化を実装しない。
  • 認証付き暗号化にはAES-GCMまたはChaCha20-Poly1305を使用する。
  • KDFとしてHKDF/PBKDF2を使用し、適切なソルトと十分なパラメータを使用する。
  • SecRandomCopyBytes を使用してキー/IVを生成する。
  • IV/ノンスを決して再利用しない。
  • 暗号化キーはKeychainに保存する。

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

  • 必要な最小限の権限を要求する。
  • 使用時点で権限を要求する。
  • 権限を要求する際は、ユーザーへの価値提案を明確に説明する。

ATT(App Tracking Transparency)

  • 必要な場合にのみATTを要求する。
  • ATTが拒否された場合はグレースフルに(gracefully)機能を低下させる。
  • フィンガープリンティング(fingerprinting)および類似の禁止されているトラッキング手法を避ける。

識別子

  • identifierForVendor を優先する。
  • ATTが付与されていない限り、IDFAを避ける。
  • 禁止されている永続的な識別子を決して使用しない。

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

  • プライバシーアノテーション付きの os_log を使用する。
  • リリースビルドでは詳細ログを無効にする。
  • ログからPIIを墨塗りする。
  • ログからシークレットを墨塗りする。
  • アナリティクスイベントからPIIとシークレットを墨塗りする。
  • クラッシュレポートからPIIとシークレットを墨塗りする。
  • 可能な場合はアナリティクスをサンプリングして集約する。
  • クラッシュのシンボリケーションがサーバー側で行われることを確認する。
  • アプリと一緒にシンボルを絶対に出荷しない。

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

トラフィック傍受テスト

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

実行時検査

  • ログにシークレット/PIIが含まれていないことを確認する。
  • Keychainアイテムが正しいACL(アクセス制御リスト)で存在することを確認する。
  • ファイルに NSFileProtectionComplete(または必要に応じてより厳格なもの)があることを確認する。

API悪用テスト

  • レート制限(rate limiting)をテストする。
  • リプレイ保護(replay protections)をテストする。
  • ページネーションの境界をテストする。
  • 大量割り当て(mass assignment)の脆弱性をテストする。
  • 詳細なエラーの漏洩をテストする。

動的解析

  • コアフローを実行しながらモバイルDASTを実行し、トランスポートの構成ミスを明らかにする。
  • DASTを使用して実行時の問題を明らかにする(例:Ostorlabを使用)。
  • トリアージと再テストのための証拠をエクスポートする。

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

SBOMと依存関係

  • Swift/Objective-CコードのSBOMを生成する。
  • 組み込みSDKのSBOMを生成する。
  • 依存関係に影響を与えるCVEをトラッキングする。
  • リスクに基づいて依存関係を定期的に更新する。

証拠のバンドル

  • スキャンアーティファクトをリリースに添付する。
  • SBOMをリリースに添付する。
  • Info.plistの差分をリリースに添付する。
  • エンタイトルメントのリストをリリースに添付する。
  • テストレポートをリリースに添付する。

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

レポート

  • エグゼクティブサマリーを作成する。
  • 範囲と方法論を文書化する。
  • PoCと証拠を含む詳細な発見事項を文書化する。
  • 発見事項にリスク評価を割り当てる。
  • 修復ガイダンスを提供する。
  • 再テストの結果を含める。

OWASP MASVSとのマッピング

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

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

  • Info.plist/ATS:任意のロードなし。厳格な例外ドメイン。必要な場合を除き、ドキュメント/ファイルの共有を無効にする。
  • Keychain:ThisDeviceOnly クラス。オプションで biometryCurrentSet/userPresence。ログアウト時にシークレットを削除。
  • ファイル:NSFileProtectionComplete。データベース/ファイルを暗号化。シークレットに UserDefaults を使用しないこと。
  • Network:HTTPSのみ。URLSessionDelegate によるピニング(バックアップ付きのSPKI SHA-256)。MITMの試みが失敗することを検証すること。
  • Auth:OIDC + PKCE。短期トークン。Keychain内のリフレッシュトークン。サーバー側での認可。IDOR/BOLAのテスト。
  • WebView:認証にはシステムブラウザを優先。WKWebView の場合、デフォルトでJSオフ、ナビゲーションの許可リスト化、アプリバウンドドメイン、機密フロー用の非永続的ストア。
  • プライバシー:バックグラウンドのスナップショットをマスキング。クリップボードのシークレットを避ける。権限を最小限に抑え、正確な使用法の文字列を維持すること。
  • リリースの衛生管理:アプリ内のシンボルを削除し、dSYMを非公開に保つ。デバッグログ/メニューの削除。エンタイトルメントを検証すること。

構成の例とスニペット

ATS(Info.plist)

  • NSAppTransportSecurity を構成する:
  • NSAllowsArbitraryLoads = false
  • NSExceptionDomains = { yourdomain.com: { NSTemporaryExceptionAllowsInsecureHTTPLoads = false, NSIncludesSubdomains = true, NSTemporaryExceptionMinimumTLSVersion = TLSv1.2 } }

ファイル保護(Swift)

  • 完全なファイル保護を使用する:
  • try data.write(to: url, options: .completeFileProtection)

Keychainアイテム(Swiftの要点)

  • Keychainアイテムを構成する:
  • kSecClass: kSecClassGenericPassword
  • kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
  • kSecAttrAccessControl: SecAccessControlCreateWithFlags(..., [.biometryCurrentSet, .userPresence])

URLSessionピニング(概要)

  • urlSession(_:didReceive:completionHandler:) 内:
  • SecTrust を評価する。
  • 証明書からSPKIを抽出する。
  • SPKIのSHA-256をピンの許可リストと比較する。
  • 適切に .useCredential または .cancel を選択する。

軽量なワークフロー

プレマージゲート(Pre-merge gate)

  • lintとテストを実行する。
  • 依存関係監査を実行する。
  • SAST/SCAスキャンをトリガーする。
  • High/Criticalな問題でマージをブロックする。

プレリリースステージング(Pre-release staging)

  • Info.plistとエンタイトルメントを検証する。
  • リリースバイナリをビルドする。
  • アプリ内にシンボルが出荷されていないことを確認する。
  • 主要なユーザーフローを実行しながらDASTを実行し(例:Ostorlabを使用)、TLS/ピニング、ストレージ保護、およびロギングの衛生状態を検証する。
  • 問題を修正し、再テストする。

リリース後の監視(Post-release monitoring)

  • クラッシュとパフォーマンスをトラッキングする。
  • 定められたスケジュールでキー/トークンをローテーションする。
  • SDKのアドバイザリとCVEを監視する。
  • サードパーティSDK/ライブラリ(例:Ostorlab)を監視し続け、新しい脆弱性を警告する。
  • 新しい脆弱性が発見された場合に的を絞った再テストをトリガーする。