"didReceiveChallenge" не вызвал аутентификацию HttpBasic после ServerTrust

Я конвертирую приложение iOS из NSURLConnection в NSURLSession.

Сервер взаимодействует с использованием как https (сертификат, подписанный признанным ЦС), так и базовая аутентификация.

Вместо использования блоков завершения для возврата данных я использую пользовательские делегаты. Я видел в другом месте, что использование пользовательских делегатов означает, что я должен отвечать на AuthenticationChallenges, а не полагаться на CredentialStorage (но это не работает, но это еще одна проблема).

Моя проблема заключается в том, что вызов возникает один раз для ServerTrust, но не вызван снова для HttpBasic Authentication. Итак, мои сеансы заканчиваются.

Я пробовал использовать блоки завершения для '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
}

Ответ 1

Без особого порядка:

  • Фактически вы полностью отключили TLS для конкретного хоста, потому что вы не проверяете сертификат каким-либо значимым образом и проверяете только то, что он предоставил сертификат, соответствующий данному имени хоста. Это очень, очень опасно. Вместо этого вам нужно выполнить некоторую выборочную проверку в сертификате (например, проверить, соответствует ли ключ прикрепленному ключу или проверяет, подписан ли он общепринятым внутренним сертификатом), и ТОГДА делать то, что вы донг ТОЛЬКО, если он проверяет.
  • Вы (я думаю) эффективно полностью сломал TLS для всех других хостов, указав, что вы не хотите разрешать серверу предоставлять свой сертификат. В этом случае вы должны запрашивать обработку по умолчанию.
  • Для любого другого типа запроса ваш метод игнорирует вызов, а это означает, что соединение будет просто вешать навсегда и никогда не продвинуться вперед. В этих случаях вы также должны запрашивать обработку по умолчанию. В противном случае, когда (а не если) вас попросят получить сертификат клиента, ваше соединение будет зависать и, в конечном итоге, просто отключится.
  • Вы неправильно обрабатываете фактическое уведомление. Вы никогда не должны вызывать методы для отправителя вызова с помощью NSURLSession. Вы должны вызвать метод завершения. В противном случае сеанс будет продолжаться до тех пор, пока вы не назовете его до тех пор, пока запрос не истечет.

Я не уверен, есть ли в коде другие ошибки, но все три из них могут привести к серьезным нарушениям, и один из них является серьезным явлением безопасности. Начните с исправления этих проблем, и если он все еще не работает, добавьте дополнительные комментарии.: -)