Ios 请对验证收据的“过于简单”的方法发表意见

Ios 请对验证收据的“过于简单”的方法发表意见,ios,in-app-purchase,receipt,Ios,In App Purchase,Receipt,我认为,说苹果的收据验证帮助被混淆是一种轻描淡写的说法 不知何故,我已经能够将一些不使用OpenSSL或ASN1的代码放在一起,这似乎可以让我访问收据字段,作为一个捆绑包的所有收据的可读字符串,包括当前设备可能尚未生成的最新收据 这是一个正在进行的工作,正如你在todo中看到的,但是有人能告诉我为什么我不应该使用这种方法,因为从我所读到的关于这个主题的所有内容来看,它似乎太容易了 还有谁能帮我做todo吗?就像我在todo1和TODO2需要做的一样,我想我可以处理3、4和5 就我而言,我正在从最

我认为,说苹果的收据验证帮助被混淆是一种轻描淡写的说法

不知何故,我已经能够将一些不使用OpenSSL或ASN1的代码放在一起,这似乎可以让我访问收据字段,作为一个捆绑包的所有收据的可读字符串,包括当前设备可能尚未生成的最新收据

这是一个正在进行的工作,正如你在todo中看到的,但是有人能告诉我为什么我不应该使用这种方法,因为从我所读到的关于这个主题的所有内容来看,它似乎太容易了

还有谁能帮我做todo吗?就像我在todo1和TODO2需要做的一样,我想我可以处理3、4和5

就我而言,我正在从最近到最早扫描“最新收据信息”收据,直到找到我感兴趣的应用内产品id,然后从“过期日期”确定其过期状态。这是确定产品id当前过期状态的正确方法吗

不管怎样,这是:

NSURL *storeURL;
exms = [[NSUserDefaults standardUserDefaults] doubleForKey:@"exms"];  //globally defined elsewhere
[[NSUserDefaults standardUserDefaults] synchronize];

NSDate *today = [NSDate date];
int64_t nowms = 1000*[today timeIntervalSince1970];
if (nowms> exms){ //todo maybe give a week grace here
   isSubscribed= NO;  //globally defined elsewhere
   // Load the receipt from the app bundle.
   NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
   NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
   if (!receipt) { /* No local receipt -- handle the error. */ }  //todo1

   /* ... Now Send the receipt data to server ... */
   // Create the JSON object that describes the request
   NSError *error;
   NSDictionary *requestContents = @{@"receipt-data": [receipt base64EncodedStringWithOptions:0],
                                     @"password": @"put your shared secret here"};
   NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents options:0 error:&error];

   if (!requestData) { /* ... Handle error ... */ } //todo2

   NSString *file=[NSHomeDirectory() stringByAppendingPathComponent:@"iTunesMetadata.plist"];
   if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {
      // probably a store app
      storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];  //todo3 test this for sure rather than ifdef debug
   }else{
      storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
   }

   NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
   [storeRequest setHTTPMethod:@"POST"];
   [storeRequest setHTTPBody:requestData];

   // Make a connection to the iTunes Store on a background queue.
   NSOperationQueue *queue = [[NSOperationQueue alloc] init];
   [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                          completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                             if (connectionError) {
                                /* ... Handle error ... */ //todo4
                             } else {
                                int64_t edms= 0;
                                NSError *error;
                                NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];

                                if (!jsonResponse) { /* ... Handle error ...*/ }   //todo5

                                // Get object from the root object
                                NSArray *dictionaryObject = (NSArray *)[jsonResponse objectForKey:@"latest_receipt_info"];

                                NSDictionary *rcpt;
                                for (int i=[dictionaryObject count]-1;i>-1;i--){
                                   rcpt= [dictionaryObject objectAtIndex:i];
                                   NSString *pid= [rcpt objectForKey:@"product_id"];
                                   if ([pid isEqualToString:@"put your in-app purchase you are interested in here"]){
                                      edms= [[rcpt objectForKey:@"expires_date_ms"] longLongValue];
                                      break;
                                   }
                                }

                                NSDate *today = [NSDate date];
                                int64_t nowms = 1000*[today timeIntervalSince1970];
                                if (nowms> edms){
                                   isSubscribed= NO;
                                }else{
                                   isSubscribed= YES;
                                }
                                exms= edms;
                                [[NSUserDefaults standardUserDefaults] setDouble:exms forKey:@"exms"];
                                [[NSUserDefaults standardUserDefaults] synchronize];

                                if (isSubscribed== YES) _isexpiredView.hidden= true;

                             }
   }];
}else{
   isSubscribed= YES;
}

收据验证的机械步骤并不难,您的实现解决了这些问题。安全地执行此操作更加困难,因为您无法信任最终用户设备

我看到的第一个问题是,您正在NSUserDefaults中存储到期日期,并且只有在到期日期之后才验证收据。因此,我可以简单地将过期日期放在NSUserDefaults中,您将永远不会使订阅过期,也不会检查过期日期是否有效。使用加密值并存储在密钥链中将使其更加安全

其次,您要验证直接从设备到苹果服务器的收据。从

使用受信任的服务器与应用商店通信。用你自己的 服务器允许您将应用程序设计为仅识别和信任您的应用程序 服务器,并允许您确保服务器与应用程序连接 存储服务器。无法在之间建立受信任的连接 用户的设备和应用商店直接连接,因为您无法控制 连接的两端

这意味着,通过直接从你的设备连接,你可以很容易地让别人欺骗苹果服务器。如果你验证了收据是由苹果公司签署的,那么这将更加困难,但事实上你盲目地相信收据上的到期日期


只要您相信您的用户是诚实的,您的方法是可以的。

感谢您花时间了解我的做法。我将该值存储在NSUserdefaults中,因此在原始收据过期之前,我不必“打扰”苹果服务器,我可以轻松删除该功能。就通过直接从设备连接进行验证而言,苹果服务器正在响应发送给它的“真实”加密嵌入式收据这一事实难道不包括这一点吗?有人如何“欺骗”它?但你不知道你正在与苹果服务器通话。你可能在和一个假装是苹果服务器的服务器交谈。您正在提供从本地设备收到的收据,您无法信任该收据,并且正在与无法验证的服务器通话。未越狱的iPhone是否可以在其上安装截获和重定向我的请求的软件,或者您是说,在我的iPhone发送请求后,所有来自我设备的请求都可以“非法”重定向?我收到的回复不是必须来自我请求的实际URL吗?抱歉,不熟悉URLrequest安全性。在越狱设备上更容易,但即使没有越狱,通过代理或DNS服务器设置拦截请求也相当简单。如果您验证数字证书和收据签名,那么您在非越狱设备上可能没有问题,因为用户不能篡改根证书存储谢谢,但是验证数字证书和收据签名对我来说是希腊语。我很惊讶。看起来这个问题已经存在了两年多了,苹果仍然希望美国开发者成为与黑客较量的对象。要做到这一点,应该有一个有良好文档记录的安全苹果方法!我想我只会相信我的用户。我甚至无法在沙箱帐户中找到关于我从请求中获得的收据的文档。我不断收到收据,看起来过期日期延长了一个小时而没有再购买。谢谢,卡门