"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. Вы должны вызвать метод завершения. В противном случае сеанс будет продолжаться до тех пор, пока вы не назовете его до тех пор, пока запрос не истечет.
Я не уверен, есть ли в коде другие ошибки, но все три из них могут привести к серьезным нарушениям, и один из них является серьезным явлением безопасности. Начните с исправления этих проблем, и если он все еще не работает, добавьте дополнительные комментарии.: -)