应用内购买收据验证矛盾

时间:2013-07-31 11:11:28

标签: ios in-app-purchase storekit receipt

Apple提供了两份关于收据验证的文件,显然是相互矛盾的陈述。

在“Verifying Store Receipts”中:

  

注意:在iOS上,商店收据的内容和格式是私有的   并且可能会有变化。 您的应用程序不应尝试解析   收据数据直接

然而,在“In-App Purchase Receipt Validation on iOS”中提供了示例代码,其中解析并验证了商店收据,作为安全漏洞的“缓解策略”的一部分:

// Check the validity of the receipt.  If it checks out then also ensure the transaction is something
// we haven't seen before and then decode and save the purchaseInfo from the receipt for later receipt validation.
- (BOOL)isTransactionAndItsReceiptValid:(SKPaymentTransaction *)transaction
{
    if (!(transaction && transaction.transactionReceipt && [transaction.transactionReceipt length] > 0))
    {
        // Transaction is not valid.
        return NO;
    }

    // Pull the purchase-info out of the transaction receipt, decode it, and save it for later so
    // it can be cross checked with the verifyReceipt.
    NSDictionary *receiptDict       = [self dictionaryFromPlistData:transaction.transactionReceipt];
    NSString *transactionPurchaseInfo = [receiptDict objectForKey:@"purchase-info"];
    NSString *decodedPurchaseInfo   = [self decodeBase64:transactionPurchaseInfo length:nil];
    NSDictionary *purchaseInfoDict  = [self dictionaryFromPlistData:[decodedPurchaseInfo dataUsingEncoding:NSUTF8StringEncoding]];

    NSString *transactionId         = [purchaseInfoDict objectForKey:@"transaction-id"];
    NSString *purchaseDateString    = [purchaseInfoDict objectForKey:@"purchase-date"];
    NSString *signature             = [receiptDict objectForKey:@"signature"];

    // Convert the string into a date
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss z"];

    NSDate *purchaseDate = [dateFormat dateFromString:[purchaseDateString stringByReplacingOccurrencesOfString:@"Etc/" withString:@""]];


    if (![self isTransactionIdUnique:transactionId])
    {
        // We've seen this transaction before.
        // Had [transactionsReceiptStorageDictionary objectForKey:transactionId]
        // Got purchaseInfoDict
        return NO;
    }

    // Check the authenticity of the receipt response/signature etc.

    BOOL result = checkReceiptSecurity(transactionPurchaseInfo, signature,
                                       (__bridge CFDateRef)(purchaseDate));

    if (!result)
    {
        return NO;
    }

    // Ensure the transaction itself is legit
    if (![self doTransactionDetailsMatchPurchaseInfo:transaction withPurchaseInfo:purchaseInfoDict])
    {
        return NO;
    }

    // Make a note of the fact that we've seen the transaction id already
    [self saveTransactionId:transactionId];

    // Save the transaction receipt's purchaseInfo in the transactionsReceiptStorageDictionary.
    [transactionsReceiptStorageDictionary setObject:purchaseInfoDict forKey:transactionId];

    return YES;
}

如果我理解正确,如果我验证收据,当Apple决定更改收据格式时,我的应用可能会停止工作。

如果我不验证收据,我不会遵循Apple的“缓解策略”,我的应用程序很容易受到攻击。

如果我这样做,该死的,如果我不这样做,该死的。有什么我想念的吗?

2 个答案:

答案 0 :(得分:3)

他们强烈建议您使用自己的服务器作为验证中介,因为这样可以清晰安全地通过App Store获取所有iOS版本。这无论如何都是不被诅咒的最佳途径。

如果您必须直接从设备执行验证到App Store,那么只有当应用程序在5.1.x及更低版本上运行时,才会使用其缓解策略。对于iOS6及更高版本,请使用提供的推荐方法。

虽然您不应该直接解析收据,但发现的漏洞使Apple在如何解决这个问题的过程中陷入困境,并决定应用程序开发人员实施检查。这意味着当用户更新应用程序时,收据现在再次受到保护(无论iOS版本如何),从而提供更好的修复范围。作为一个副作用,这意味着你必须打破通常的做法(但Apple允许你这样做)。

我同意文档在这方面并不完全清楚,并且可以进一步澄清(您应该从底部的文档页面给出反馈)。 Apple发布了缓解策略,他们确实表示应该在iOS 5.1.x及更低版本上使用它来解决漏洞。如果他们改变IAP收据的格式/内容,他们就有责任。

答案 1 :(得分:0)

Apple目前还在推荐设备收据验证。请参阅Validating receipts locally和WWDC 2013 talk Using receipts to protect your digital sales