在ServerTrust之后,'didReceiveChallenge'没有被要求进行HttpBasic身份验证

Sni*_*ips 10 nsurlconnection ios nsurlsession

我正在将一个iOS应用程序从NSURLConnection转换为NSURLSession.

服务器与使用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
}
Run Code Online (Sandbox Code Playgroud)

dga*_*ood 5

排名不分先后:

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

我不确定代码中是否还存在其他错误,但这三个错误都可能导致严重的不当行为,其中之一是主要的安全漏洞。首先解决这些问题,如果仍然不起作用,请添加更多评论。:-)