我正在将 iOS 应用程序从 NSURLConnection 转换为 NSURLSession。
服务器使用 https(由公认的 CA 签名的证书)和基本身份验证进行交互。
我没有使用完成 block 来返回数据,而是使用自定义委托(delegate)。我在别处看到,使用自定义委托(delegate)意味着我应该响应 AuthenticationChallenges 而不是依赖 CredentialStorage(这也行不通,但那是另一个问题)。
我的问题是,对 ServerTrust 进行了一次质询,但对 HttpBasic 身份验证不会再次调用。所以,我的 session 超时了。
我已经尝试为“defaultSession dataTaskWithRequest”使用完成 block 而不是自定义委托(delegate),只是想看看我是否可以通过这一点,但这没有什么区别。我还尝试将 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
}
最佳答案
排名不分先后:
- 您实际上已经为相关的特定主机完全禁用了 TLS,因为您没有以任何有意义的方式检查证书,而只是检查它是否提供了与给定主机名匹配的证书。这是非常非常危险的。相反,您需要对证书执行一些自定义验证(例如,检查 key 是否与固定 key 匹配或检查它是否由已知可信的内部证书签名),然后仅在验证时执行您正在执行的操作.
- 通过指定您不想让服务器提供其证书,您(我认为)有效地完全破坏了所有其他主机的 TLS。在这种情况下,您应该要求进行默认处理。
- 对于任何其他挑战类型,您的方法是忽略挑战,这意味着连接将永远挂起,永远不会取得进展。在这些情况下,您也应该要求进行默认处理。否则,当(而不是如果)您被要求提供客户端证书时,您的连接将似乎挂起并最终会超时。
- 您对实际通知的处理有误。您应该永远不要使用 NSURLSession 调用质询发送者的方法。您必须调用完成方法。否则, session 将继续等待您调用它,直到请求超时。
我不确定代码中是否还有其他错误,但所有这三个错误都可能导致严重的不当行为,其中一个是主要的安全漏洞。首先解决这些问题,如果仍然不起作用,请添加更多评论。 :-)
关于ios - 在 ServerTrust 之后不调用 'didReceiveChallenge' 进行 HttpBasic 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41417558/