WWDC Quick Look 💓 By SwiftGGTeam
Mitigate fraud with App Attest and DeviceCheck

Mitigate fraud with App Attest and DeviceCheck

元の動画を見る

ハイライト

Apple は、DeviceCheck と App Attest を使用して、デバイス レベルのプロモーション ステータス、アプリケーションの整合性検証、およびリクエスト署名機能を提供し、サーバーが重複したクレーム、変更されたクライアント、および改ざんされた機密リクエストを識別できるようにします。

主要内容

一般的なプロモーションは、新機能の無料アイテムを送ることです。問題はインストール パッケージ自体にあります。ユーザーはアプリをアンインストールし、再インストールして、再度申請することができます。アカウント システムは一部の不正行為をブロックできる場合がありますが、アカウントの変更、データの消去、同じデバイスでのアプリの再インストールは、依然として多くの単純なルールをバイパスします。

DeviceCheck は、この種のプロモーション ステータスの問題を解決します。これにより、開発者はデバイスの 2 ビットとタイムスタンプを Apple サーバーに保存できます。この状態は、アプリの再インストール、デバイスの転送、さらにはすべてのコンテンツと設定の消去を行っても持続します。 「すでに初心者ギフトパックを受け取った」「不正行為に参加した」など、ビットの意味は開発者自身が決定します。

もう一つの質問はもっと難しいです。サーバーがリクエストを受信した場合、それが正規のアプリからのものであることを確認することは困難です。再パッケージ化されたクライアントは地理的位置を偽装し、旅行コレクション アプリケーションですべてのコレクションを収集できます。ゲーム プラグインは無制限のアクセラレーションを提供します。スクリプトがインターフェイスに直接アクセスし、サーバーがトラフィックが実際のユーザーからのものであると誤って認識する可能性もあります。

App Attestはリクエスト元とリクエスト内容の問題を解決します。これにより、アプリは Apple が認定するハードウェアで保護されたキーを作成できます。サーバー側の検証後、キーが実際の Apple デバイスから取得され、実際のアプリに属し、後続のリクエストのペイロードが途中で変更されていないことがわかります。

このメカニズムの境界も非常に明確です。 App Attest のキーはアプリのインストールごとに生成され、再インストール後は保持されず、バックアップや同期もされません。 DeviceCheck は、デバイスレベルの履歴ステータスを記録するのに適しています。 App Attest は、重要ではあるが頻度の低いサーバー リクエストを保護するのに適しています。

詳細

DeviceCheck: デバイスレベルの状態を Apple サーバーに保存します

(00:50) DeviceCheck は、プロモーションの悪用を軽減するために iOS 11 で導入されたフレームワークです。これにより、デバイスに関連付けられた 2 ビットとタイムスタンプが Apple サーバーに保存されます。 Apple は状態を維持する責任があり、開発者は状態の意味を定義する責任があります。

import DeviceCheck

let device = DCDevice.current

if device.isSupported {
    device.generateToken { token, error in
        guard let token = token, error == nil else {
            // Handle the error.
            return
        }

        // Send token to your server.
    }
} else {
    // Treat as a risk signal in your fraud assessment.
}

キーポイント:

  • import DeviceCheckDeviceCheck フレームワークの紹介。
  • DCDevice.current現在のデバイスの DeviceCheck エントリを取得します。
  • device.isSupportedまず、デバイスが DeviceCheck をサポートしているかどうかを確認します。
  • generateTokenデバイス トークンを生成すると、アプリはそれを独自のサーバーに送信します。
  • サーバーはトークンを使用して Apple の DeviceCheck サービスを呼び出し、デバイスの 2 ビットをクエリまたは更新します。
  • サポートされていない場合、または生成に失敗した場合、機密性の高い権利や利益を直接解放しないでください。失敗はリスク制御のシグナルとして使用される必要があります。

これら 2 つのビットは、同じ開発者アカウント内のすべてのアプリで共有されます。意味を設計するときは、製品ライン全体をカバーする必要があり、単一のアプリのローカル ロジックに基づいて名前を付けることはできません。タイムスタンプは、アクティビティ サイクルごとに 1 回クレームを再許可するなど、定期的なリセットに使用できます。

アプリの認証ステップ 1: ハードウェアで保護されたキーを作成する

(08:02) App Attest は DeviceCheck フレームワークに属します。その中心となるのは 1 対のセキュリティ キーです。秘密キーは、App Attest API を介して Secure Enclave でのみアクセスできます。次に、サーバーは公開キーを使用してリクエストの署名を検証します。

let appAttestService = DCAppAttestService.shared

if appAttestService.isSupported {
    appAttestService.generateKey { keyId, error in
        guard error == nil else { /* Handle the error. */ }
        // Cache keyId for subsequent operations.
    }
} else {
   // Handle fallback as untrusted device
}

キーポイント:

  • DCAppAttestService.sharedApp Attest サービス オブジェクトを取得します。
  • appAttestService.isSupportedまずサポートを確認する必要があります。
  • App Attest には Secure Enclave を備えたデバイスが必要ですが、App Extension やその他のシナリオが引き続き返される可能性がありますfalse
  • generateKeyデバイス上にアプリ認証キーを作成します。
  • keyIdは後続の証明キーと生成されたアサーションのインデックスであり、アプリはそれをキャッシュする必要があります。
  • サポートされていない場合、Apple は、最初に発信者を信頼できないものとして分類し、その後全体的なリスク制御ロジックに判断を委ねることをお勧めします。

ここには置かないでくださいisSupported == false唯一の禁止条件として。この講演では、「App Attest Not Supported」の割合を監視することが提案されています。この比率が突然低下した場合は、再パッケージ化されたアプリがチェックをバイパスしようとしている可能性があります。

アプリの証明ステップ 2: キーが実際のデバイスと実際のアプリに属していることを証明する

(09:17) キーが作成された後は、それを直接信頼することはできません。サーバーは最初に 1 回限りのチャレンジを送信します。アプリはチャレンジ、ユーザー アカウント ID、またはその他のビジネス値を一緒にハッシュして取得します。clientDataHash。このハッシュは証明プロセスを現在のビジネス コンテキストにバインドし、仲介者やリプレイ攻撃を防ぎます。

appAttestService.attestKey(keyId, clientDataHash: clientDataHash) { attestationObject, error in
    guard error == nil else { /* Handle error. */ }

    // Send the attestation object to your server for verification.
}

キーポイント:

  • keyId前のステップからgenerateKey
  • clientDataHashサーバー側のチャレンジとビジネス データ ハッシュから取得されます。
  • attestKey秘密キーを使用してハードウェア認証リクエストを作成し、検証のために Apple に送信します。
  • Apple の検証後に匿名で返却attestationObject。 -アプリハンドルattestationObjectビジネスペイロードを独自のサーバーに送信します。
  • サーバー検証に合格すると、App Attest キーが保存され、後続のリクエストの検証に使用されます。

(10:22) サーバーは Web 認証の標準形式を検証します。証明オブジェクトには、Apple 署名証明書チェーン、認証データ、リスク メトリック受信の 3 種類のコンテンツが含まれています。

struct AppAttestVerificationInput {
    let attestationObject: Data
    let clientDataHash: Data
    let expectedAppIdentifier: String
    let keyId: String
}

func verifyAttestation(_ input: AppAttestVerificationInput) throws {
    // 1. Validate the certificate chain against the App Attest root certificate.
    // 2. Reconstruct the nonce from clientDataHash and verify it in the leaf certificate.
    // 3. Validate the app identity hash in authenticator data.
    // 4. Store the public key and risk metric receipt for later requests.
}

キーポイント:

  • attestationObjectアプリによって送信される証明書オブジェクトです。
  • clientDataHashアプリで呼び出す必要がありますattestKey使用されるハッシュは一貫しています。
  • expectedAppIdentifierサーバーが期待するアプリ ID を表します。
  • 最初のステップは、証明書チェーンを検証することです。ルート証明書は Apple プライベート PKI リポジトリから取得されます。
  • 2 番目のステップでは、ノンスを再構築し、それがリーフ証明書に表示されることを確認します。
  • 3 番目のステップは、認証データのアプリ ID ハッシュをチェックして、リクエストがアプリからのものであることを確認することです。
  • 4 番目のステップは、後続のアサーションとリスク評価のために公開キーとリスク メトリックの受信を保存することです。

また、証明できないことが必ずしも攻撃を示すわけでもありません。スピーチで挙げられた通常の失敗には次のようなものがあります。isSupportedのためにfalse、グレースケール期間中に電流が制限され、ネットワークに障害が発生します。オンラインにする場合は、多数の設備からの同時通話を避けるために、カバー範囲を徐々に拡大する必要があります。attestKey電流制限をトリガーします。

アプリ検証ステップ 3: 機密リクエストのアサーションを生成する

(13:14) キーの認証が完了すると、後続のリクエストではgenerateAssertion。このプロセスは Apple サーバーにアクセスしなくなります。アサーションはデバイス上で生成され、以前に保存された公開キーを使用してサーバーによって検証されます。

appAttestService.generateAssertion(keyId, clientDataHash: clientDataHash) { assertionObject, error in
    guard error == nil else { /* Handle error. */ }

    // Send assertion object with your data to your server for verification
}

キーポイント:

  • リクエストの前に、サーバーは固有のチャレンジを送信します。
  • アプリはペイロードとチャレンジのダイジェストを生成します。clientDataHash
  • generateAssertionApp Attest 秘密キーを使用してこのダイジェストに署名します。
  • assertionObject元のビジネス データと一緒にサーバーに送信する必要があります。
  • サーバーは nonce を再構築し、保存された公開キーを使用して署名を検証します。
  • 署名が有効な場合、サーバーはペイロードが改ざんされていないことを信頼できます。

(14:09) アサーション オブジェクトには、署名と認証データが含まれています。サーバーはアプリ ID ハッシュもチェックし、増加するカウンターを保存します。リプレイ攻撃のリスクを軽減するには、次のリクエストのカウンタを大きくする必要があります。

struct AppAttestAssertionInput {
    let assertionObject: Data
    let payload: Data
    let challenge: Data
    let storedPublicKey: Data
    let previousCounter: UInt32
}

func verifyAssertion(_ input: AppAttestAssertionInput) throws {
    // 1. Recreate clientDataHash from payload and challenge.
    // 2. Reconstruct the nonce from clientDataHash.
    // 3. Verify the signature with the stored public key.
    // 4. Validate the app identity hash in authenticator data.
    // 5. Confirm the counter is greater than previousCounter, then store it.
}

キーポイント:

  • payload保護する必要があるのは、賞の受賞、引き換え、結果のアップロードなどのビジネス データです。
  • challengeこれは、このリクエストに対してサーバーによって生成される 1 回限りの値です。
  • storedPublicKey前の鍵認証段階から。
  • previousCounterこれはサーバーによって保存された最後のラウンドカウンターです。
  • 署名検証により、ペイロードとチャレンジが置き換えられていないことが確認されます。
  • アプリ ID ハッシュ検証により、アサーションがアプリからのものであることが確認されます。
  • カウンタ増分チェックは、古いアサーションの重複送信を検出するために使用されます。

アサーションは暗号化操作であり、待ち時間が追加されます。講演では、重要な結果の引き換え、受信、送信など、重要だが頻度は低い通話にこれを使用することを提案しました。リアルタイムの高頻度ネットワーク コマンドは、毎回アサーションを生成するのには適していません。

リスク メトリック サービス: デバイスのバッチ製造キーを発見しました

(16:04) 攻撃者は実際のデバイスを使用して多くの App Attest キーを作成し、このデバイスをトランジットとして使用して、多くの変更されたアプリに通信を提供する可能性があります。 App Attest Risk Metric Service は、そのようなパターンを検出するために使用されます。

struct RiskMetricRecord {
    let keyId: String
    let receipt: Data
    let approximateKeyCount: Int?
}

func updateRiskMetric(_ record: RiskMetricRecord) {
    // Submit the latest receipt to the App Attest Risk Metric Service.
    // Store the new receipt and the approximate number of keys for this app/device pair.
}

キーポイント:

  • attestKey返された証明書には、リスク メトリックの受信が含まれます。
  • サーバーはこのレシートを保存します。
  • 後続のサーバーは、新しいレシートと引き換えにレシートをリスク指標サービスに送信します。
  • 新しいレシートには、このデバイスによってアプリ用に作成されたキーのおおよその数が含まれます。
  • サーバーは定期的に最新のレシートを交換し、同じアプリ/デバイスのペアのリスクシグナルを更新できます。

この指標は、リスク管理スコアを入力するのに適しています。ビジネスルールに代わるものではありません。デバイス上のキーの数が異常な場合、追加の検証を要求したり、報酬額を減額したり、機密の権利や利益の発行を遅らせたりすることができます。

App Clip: App Clip とフルアプリは App Attest ID を共有します

(17:17) iOS 15 では、App Clip の App Attest サポートが追加されています。 App Clip を完全な App に正常にアップグレードするには、App Clip と完全な App が App Attest コンテキストで同じ App ID を共有する必要があります。

struct AppIdentityPolicy {
    let fullAppIdentifier: String
    let appClipIdentifier: String

    func accepts(_ attestedAppIdentity: String) -> Bool {
        attestedAppIdentity == fullAppIdentifier || attestedAppIdentity == appClipIdentifier
    }
}

キーポイント:

  • fullAppIdentifier完全なアプリの ID を表します。
  • appClipIdentifierApp ClipのIDを示します。
  • サーバーは、アプリの ID を検証するときに、App Clip のアップグレード パスを考慮する必要があります。
  • 手動で削除するか、期限切れの App Clip を使用すると、App Attest キーが無効になります。
  • 完全なアプリをアンインストールすると、そのアプリ認証キーも無効になります。

重要ポイント

  1. やるべきこと: 初心者パッケージに「デバイスごとに 1 回」の制限を追加します。 実行する価値がある理由: DeviceCheck の 2 ビットは再インストール後も保持されるため、報酬を受け取るためにアンインストールとインストールを繰り返すシナリオをカバーできます。 開始方法: を使用しますDCDevice.current.generateTokenデバイス トークンをサーバーに送信すると、サーバーは DeviceCheck API を使用して「受信」ビットをクエリおよび更新します。

  2. やるべきこと: 引換コードを保護し、ポイントを引き出し、レアアイテムのインターフェースを受け取ります。 実行する価値がある理由: App Attest は、ペイロード、サーバー チャレンジ、デバイス キーをバインドして、クライアントの偽造リクエストが変更されるリスクを軽減できると主張します。 開始方法: を使用しますDCAppAttestService.generateKeyキーを作成し、attestKeyサーバーの登録を完了し、機密インターフェイスを呼び出す前に使用してください。generateAssertionアサーションを生成します。

  3. やるべきこと: リーダーボードの送信にクライアントの整合性シグナルを追加します。 実行する価値がある理由: サーバーは、リクエストが実際の Apple デバイスと実際のアプリから送信されたものであることを確認し、その結果をランキングに直接含めるかどうかを決定できます。 開始方法: スコア ペイロードと 1 回限りのチャレンジをハッシュして呼び出しますgenerateAssertion、サーバーは署名、アプリ ID ハッシュを検証し、カウンターをインクリメントします。

  4. 対処方法: 「App Attest をサポートしていない」デバイスの割合を監視します。 実行する価値がある理由: この講演では、スケールの突然の変更は、再パッケージ化されたアプリによるチェックを回避しようとする試みを表している可能性があることを指摘しています。 開始方法: サーバー側で記録するisSupported == false、アテステーションの失敗、アサーションの失敗、その他のイベントについて、バージョン、リージョン、デバイス タイプごとの傾向を観察します。

  5. やるべきこと: App Clip の割引をアップグレードして保護します。 実行する価値がある理由: iOS 15 では、App Attest を使用するための App Clip がサポートされています。 App Clip とフル App は、App Attest ID を共有します。これは、軽量エクスペリエンスからフルインストールまでのエクイティリンクを保護するのに適しています。 開始方法: アプリの ID を検証するとき、サーバーはアプリ クリップと完全なアプリの両方の法的 ID を受け入れます。権利の発行では、引き続き DeviceCheck またはアカウント ステータスを使用して重複を削除します。

関連セッション

コメント

GitHub Issues · utterances