2017-01-01 1 views
10

Я конвертирую приложение iOS из NSURLConnection в NSURLSession.'didReceiveChallenge' не вызвал аутентификацию HttpBasic после ServerTrust

Сервер взаимодействует с использованием как 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 
} 

ответ

4

В частности, нет порядка:

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

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

+0

Спасибо за ваш ответ, я ценю, что вы нашли время ответить. Мое приложение только когда-либо говорит на моем собственном сервере, поэтому, на данный момент, по крайней мере, меня интересует только проверка сертификата для этого защитного места. Для других типов запросов я обрабатываю (и ожидаю) HTTPBasic, который никогда не приходит. Если бы у меня были другие, которые не были обработаны, я бы получил диагностику, но я этого не делаю. Еще раз спасибо. – Snips

+0

Извините, я не заметил четвертую проблему в первый раз, когда я посмотрел на это. Вы вызываете методы для отправителя вызова, который * не * действительный способ делать вещи с NSURLSession. Вы * должны * вызывать метод завершения с выбранным расположением и, необязательно, учетные данные.В противном случае ваши запросы будут все время ждать, пока ваш код вызовет это завершение. – dgatwood

Смежные вопросы