2017-01-01 148 views
10

我正在将iOS应用程序从NSURLConnection转换为NSURLSession。'didReceiveChallenge'在调用ServerTrust之后不会调用HttpBasic身份验证

服务器与同时使用https(由已识别的CA签名的证书)和基本验证进行交互。

而不是使用完成块的数据返回,我使用自定义代表。我在其他地方看到过,使用自定义代表意味着我应该对AuthenticationChallenges做出响应,而不是依赖于CredentialStorage(不是那也适用,但那是另一个问题)。

我的问题是,对于ServerTrust而言,发生一次挑战,但不会再次为HttpBasic身份验证调用。所以,我的会议超时。

我已经尝试使用完成块为'defaultSession dataTaskWithRequest'而不是自定义的代表,只是为了看看我能否通过这一点,但它没有区别。我也尝试使用CredentialStorage作为HttpBasic凭证,但如上所述,没有喜悦。

这让我难住了。有任何想法吗?

(void)URLSession:(NSURLSession *)connection 
// task:(NSURLSessionTask *)task 
    didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
    completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler 
{ 
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) 
    { 
#if 1 && defined(DEBUG) 
     NSLog (@"didReceiveChallenge: Using SSL"); 
#endif // DEBUG 

     if ([challenge.protectionSpace.host isEqualToString:HOST]) 
     { 
#if 1 && defined(DEBUG) 
      NSLog (@"didReceiveChallenge: Using Protection Space Host - %@", HOST); 
#endif // DEBUG 

      [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge]; 
     } 
     else 
     { 
      [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge]; 
     } 
    } 
    else 

    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic] || 
     [challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) 
    { 
#if 1 && defined(DEBUG) 
     NSLog (@"didReceiveChallenge: (Basic/Digest) #%ld - user: %@, password: %@", 
       (long)[challenge previousFailureCount], USERNAME, PASSWORD); 

#endif // DEBUG 

     if ([challenge previousFailureCount] == 0) 
     { 
#if 1 && defined(DEBUG) 
      NSLog (@"didReceiveChallenge: previousFailureCount == 0"); 
#endif // DEBUG 

      NSURLCredential *newCredential; 

      newCredential = [NSURLCredential credentialWithUser:USERNAME 
          password:PASSWORD 
          persistence:NSURLCredentialPersistenceForSession]; 

      [[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge]; 

     } 
     else 
     { 
      [[challenge sender] cancelAuthenticationChallenge:challenge]; 

      // inform the user that the user name and password 
      // in the preferences are incorrect 

#if 1 && defined(DEBUG) 
      NSLog (@"didReceiveChallenge: Failed Authentication"); 
#endif // DEBUG 

      // ...error will be handled by connection didFailWithError 
     } 
    } 
#ifdef DEBUG 
    else 
    { 
     NSLog(@"didReceiveChallenge: Not handled!"); 
    } 

#endif // DEBUG 
} 

回答

4

没有特定的顺序:

  • 您已经有效地完全禁用TLS有问题的特定主机,因为你不检查任何有意义的方式证书,只是检查它提供了一个匹配给定主机名的证书。这非常非常危险。相反,您需要对证书执行一些自定义验证(例如,检查密钥是否与固定密钥匹配,或者检查是否由已知可信任的内部证书签名),然后执行只有在验证通过的情况下执行的操作。
  • 您已经(我认为)通过指定您不希望允许服务器提供其证书来有效地完全破坏所有其他主机的TLS。在这种情况下,你应该要求默认处理。
  • 对于任何其他挑战类型,您的方法忽略了挑战,这意味着连接将永远挂起,永远不会取得进展。在这些情况下,您也应该要求默认处理。否则,如果(如果)您被要求提供客户端证书,您的连接将显示为挂起并最终超时。
  • 你正在处理实际的通知错误。你应该从来没有用NSURLSession在挑战发件人上调用方法。你必须必须调用完成方法。否则,会话将继续等待您调用它,直到请求超时。

我不确定代码中是否存在其他错误,但所有这三个错误都可能导致严重的错误行为,其中一个错误是主要的安全漏洞。从解决这些问题开始,如果仍然无效,请添加更多评论。 :-)

+0

感谢您的回复,感谢您花时间回复。我的应用程序只能对我自己的服务器说话,所以至少现在我只关心验证该保护空间的证书。对于其他挑战类型,我正在处理(并期待)一个永远不会到来的HTTPBasic。如果我有其他人没有处理,我会得到一个诊断,但我不知道。再次感谢。 – Snips

+0

对不起,我第一次看到这个时没有注意到第四个问题。您正在对挑战发件人调用方法,这是*不是用NSURLSession做的事情的有效方法。您*必须*通过选择的处置方式和可选的凭证来调用完成方法。否则,您的请求将全部等待您的代码调用完成。 – dgatwood

相关问题