付费iOS应用服务器端服务的收据校验与防重放攻击问询
iOS Paid App Receipt Validation & Replay Attack Prevention
Hey there! Let's walk through how to validate user validity with your ProductionSandbox receipt and block replay attacks, since you're working with a paid iOS app (no in-app purchases, just upfront payment).
Validating User Effectiveness
First, let's break down how to confirm the receipt is legitimate for your app:
- Send receipts to the correct Apple endpoint: For
ProductionSandboxreceipts, you must send validation requests tohttps://sandbox.itunes.apple.com/verifyReceipt(production useshttps://buy.itunes.apple.com/verifyReceipt). Sending a sandbox receipt to the production endpoint will trigger a 21007 error, so double-check this. - Verify core app identifiers: Even though fields like
adam_idandapp_item_idshow 0 in sandbox receipts (this is normal for testing!), confirm thebundle_idmatches your app's exact bundle ID (e.g.,com.example.myapp) andapplication_versionaligns with your app's current version. This ensures the receipt was generated for your app specifically. - Check the
receipt_type: For sandbox testing, this should beProductionSandbox; in production, it'll beProduction. This helps you confirm you're handling the right environment. - Validate the Apple response status: After sending the receipt, Apple returns a
statusfield. A value of0means the receipt is valid. Other codes indicate specific issues:- 21002: Malformed receipt data
- 21003: Receipt can't be authenticated
- 21007: Sandbox receipt sent to production endpoint (switch to the sandbox URL!)
- Confirm purchase timestamps: Check
receipt_creation_dateandoriginal_purchase_dateto ensure the purchase is a valid, non-expired transaction (for paid apps, this is a one-time purchase, so these timestamps just confirm the transaction occurred).
Preventing Replay Attacks
Replay attacks happen when an attacker reuses a valid receipt to access your service multiple times. Here's how to stop them:
- Track unique transaction IDs: Every valid receipt (even sandbox ones) has an
original_transaction_id(found in thein_apparray of Apple's validation response, or in the main receipt fields for paid app purchases). This ID is unique to each user's original purchase and won't repeat. - Store verified IDs in your database: When you successfully validate a receipt, save the
original_transaction_idalong with thereceipt_type(to separate sandbox/production records) in your server's database. Mark it as "already verified". - Check for existing IDs before validation: Every time you receive a new receipt validation request, first query your database for the
original_transaction_id. If it exists, reject the request—this means the receipt has already been used. If it doesn't exist, proceed with validation, then store the ID once it passes. - Optional: Add device context (carefully): While not as reliable as transaction IDs, you can optionally pair the transaction ID with a user's device identifier (like the app's vendor ID) for extra security. Just note that device identifiers can change if the user reinstalls the app, so don't rely on this alone.
Key Notes
- Never validate receipts client-side: Client-side code can be tampered with, so all validation must happen on your server.
- Handle retries gracefully: Apple's verification service might return 5xx errors temporarily. Implement a retry mechanism with exponential backoff to avoid being throttled.
- Sandbox testing quirks: Sandbox receipts often have placeholder values (like 0 for
adam_id)—this is expected and doesn't mean the receipt is invalid.
内容的提问来源于stack exchange,提问作者Dan Fabulich
相关产品推荐
相关产品推荐

