Як використовувати NSURLConnection для з'єднання з SSL для ненадійного сертифіката?


300

У мене є такий простий код для підключення до веб-сторінки SSL

NSMutableURLRequest *urlRequest=[NSMutableURLRequest requestWithURL:url];
[ NSURLConnection sendSynchronousRequest: urlRequest returningResponse: nil error: &error ];

За винятком помилок, якщо cert є автономним підписом Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".Чи є спосіб встановити його так, щоб він прийняв з'єднання в будь-якому випадку (так само, як у веб-переглядачі, який ви можете натиснути accept) або спосіб його обійти?

Відповіді:


415

Існує підтримуваний API для цього! Додайте щось подібне до свого NSURLConnectionделегата:

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
  return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
  if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
    if ([trustedHosts containsObject:challenge.protectionSpace.host])
      [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

  [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

Зауважте, що ви connection:didReceiveAuthenticationChallenge:можете надіслати своє повідомлення до challenge.sender (значно) пізніше, після того, як при необхідності представить користувачеві діалогове вікно тощо


31
Велике спасибі, це працює чудово. Просто видаліть два ifs і збережіть лише частину useCendential у зворотному виклику didReceiveAuthentificationChallenge, якщо ви хочете прийняти будь-який https-сайт.
yonel

19
що таке довірений хост, де n як визначений об'єкт
Амея,

7
Амея, це був би NSArray об'єктів NSString. Рядки - це назви хостів типу @ "google.com".
Вільям Денніс

19
Цей код добре працює. Але зауважте, що суть справжніх сертифікатів полягає в тому, щоб запобігти атакам "посередників". Тому будьте в курсі, якщо ви використовуєте цей код, хтось може підробляти так званого "надійного хоста". Ви все ще отримуєте функції шифрування даних SSL, але ви втрачаєте функції перевірки ідентифікації хоста.
Вільям Денніс

42
Зараз ці методи вважаються застарілими як для iOS 5.0, так і для Mac OS X 10.6. -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challengeМетод повинен бути використаний замість.
Ендрю Р.

36

Якщо ви не бажаєте (або не можете) використовувати приватні API, існує бібліотека з відкритим кодом (ліцензія BSD) під назвою ASIHTTPRequest, яка забезпечує обгортку навколо нижнього рівня CFNetwork APIs. Нещодавно вони представили можливість дозволу HTTPS connectionsвикористовувати самопідписані або ненадійні сертифікати за допомогою -setValidatesSecureCertificate:API. Якщо ви не хочете охоплювати всю бібліотеку, ви можете використовувати джерело як орієнтир для самостійної реалізації тієї ж функціональності.


2
Тіме, ти можеш виявити, що ти хочеш використовувати асинхронізацію з інших причин (як-от можливість показу панелі прогресу), я вважаю, що для всіх, окрім самих простих запитів, саме так. Тож, можливо, вам слід просто застосувати Async зараз і зберегти клопоту пізніше.
Вільям Денніс

Дивіться це для реалізації (але використовуйте [r setValidatesSecureCertificate: NO];): stackoverflow.com/questions/7657786/…
Сем Бродкін

Вибачте, що я підняв цю тему. Але оскільки iOS 5 представив функції ARC. Як я можу зробити цю роботу зараз?
Мелвін Лай

Чи не могли б ви перевірити це: stackoverflow.com/q/56627757/1364053
nr5

33

В ідеалі мають бути лише два сценарії, коли програмі iOS потрібно буде прийняти неперевірений сертифікат.

Сценарій A: Ви підключені до тестового середовища, яке використовує сертифікат самопідписання.

Сценарій B: Ви проксі- HTTPSтрафік використовуєте за допомогою MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.проксі-сервера поверне сертифікат, підписаний самопідписаним ЦА, щоб проксі-сервер міг захоплювати HTTPSтрафік.

Господарі виробництва ніколи не повинні використовувати недовірені сертифікати з зрозумілих причин .

Якщо вам потрібно, щоб симулятор iOS прийняв неперевірений сертифікат для цілей тестування, настійно рекомендується не змінювати логіку програми, щоб відключити перевірку вбудованого сертифіката, що надається NSURLConnectionAPI. Якщо додаток буде випущено для широкого загалу, не знімаючи цієї логіки, воно буде сприйнятливим до атак людини в середині.

Рекомендований спосіб прийняття недовірених сертифікатів для цілей тестування - імпорт сертифіката Certificate Authority (CA), який підписав сертифікат, на ваш iOS Simulator або iOS-пристрій. Я написав швидкий пост у блозі, який демонструє, як це зробити у iOS Simulator за адресою:

прийняття ненадійних сертифікатів за допомогою симулятора ios


1
Дивовижна людина. Я погоджуюся, так просто забути про відключення цієї спеціальної логіки додатка, щоб прийняти будь-який ненадійний сертифікат.
Томаш

"В ідеалі має бути лише два сценарії, коли додатку iOS потрібно буде прийняти неперевірений сертифікат." - Як щодо відхилення «заявленого» хорошого сертифікату під час закріплення сертифікату? Конференція: Dignotar (pwn'd) і Trustwave (MitM Fame).
jww

Повністю згоден з вашим твердженням про забуття видалити код. Іронія полягає в тому, що набагато простіше зробити цю зміну коду, ніж змусити тренажер прийняти самопідписані серти.
devios1

12

NSURLRequestмає приватний метод setAllowsAnyHTTPSCertificate:forHost:, який називається саме тим, що ви хочете. Ви можете визначити allowsAnyHTTPSCertificateForHost:метод за NSURLRequestдопомогою категорії та встановити його для повернення YESдля хоста, який ви хочете замінити.


Звичайні застереження щодо незадокументованих API застосовуються ... але добре знати, що це можливо.
Стівен Дарлінгтон

Так, абсолютно. Я додав ще одну відповідь, яка не передбачає використання приватних API.
Натан де Вріс

Чи працює це, коли ви використовуєте "NSURLConnection sendSynchronousRequest:"?
Тім Бют

11

Щоб доповнити прийняту відповідь, для набагато кращої безпеки ви можете додати свій серверний сертифікат або власний кореневий сертифікат CA в брелок ( https://stackoverflow.com/a/9941559/1432048 ), однак це зробити самостійно не призведе до NSURLConnection автоматично засвідчує ваш самопідписаний сервер. Вам все-таки потрібно додати наведений нижче код до вашого делегата NSURLConnection, він скопійований з коду зразка Apple AdvancedURLConnections , і вам потрібно додати два файли (Credentials.h, Credentials.m) з коду зразка яблука до своїх проектів.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}

- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
//        if ([trustedHosts containsObject:challenge.protectionSpace.host])

    OSStatus                err;
    NSURLProtectionSpace *  protectionSpace;
    SecTrustRef             trust;
    SecTrustResultType      trustResult;
    BOOL                    trusted;

    protectionSpace = [challenge protectionSpace];
    assert(protectionSpace != nil);

    trust = [protectionSpace serverTrust];
    assert(trust != NULL);
    err = SecTrustEvaluate(trust, &trustResult);
    trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));

    // If that fails, apply our certificates as anchors and see if that helps.
    //
    // It's perfectly acceptable to apply all of our certificates to the SecTrust
    // object, and let the SecTrust object sort out the mess.  Of course, this assumes
    // that the user trusts all certificates equally in all situations, which is implicit
    // in our user interface; you could provide a more sophisticated user interface
    // to allow the user to trust certain certificates for certain sites and so on).

    if ( ! trusted ) {
        err = SecTrustSetAnchorCertificates(trust, (CFArrayRef) [Credentials sharedCredentials].certificates);
        if (err == noErr) {
            err = SecTrustEvaluate(trust, &trustResult);
        }
        trusted = (err == noErr) && ((trustResult == kSecTrustResultProceed) || (trustResult == kSecTrustResultUnspecified));
    }
    if(trusted)
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}

[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}

10

Я не можу взяти на це жодної заслуги, але ця, яку я знайшов, працювала дуже добре для моїх потреб. shouldAllowSelfSignedCertмоя BOOLзмінна. Просто додайте до свого NSURLConnectionделегата, і ви повинні бути роліном для швидкого обходу на основі кожного з'єднання.

- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)space {
     if([[space authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) {
          if(shouldAllowSelfSignedCert) {
               return YES; // Self-signed cert will be accepted
          } else {
               return NO;  // Self-signed cert will be rejected
          }
          // Note: it doesn't seem to matter what you return for a proper SSL cert
          //       only self-signed certs
     }
     // If no other authentication is required, return NO for everything else
     // Otherwise maybe YES for NSURLAuthenticationMethodDefault and etc.
     return NO;
}

10

У iOS 9 підключення SSL не вдасться для всіх недійсних або самопідписаних сертифікатів. Це поведінка за замовчуванням нової безпеки транспорту додатків функції в iOS 9.0 або новішої версії, а також у ОС X 10.11 та новіших версій.

Ви можете перевизначити цю поведінку в Info.plist, встановивши NSAllowsArbitraryLoadsдля YESв NSAppTransportSecurityсловнику. Однак я рекомендую змінити цей параметр лише для тестування.

введіть тут опис зображення

Для отримання інформації див. Технічний приклад щодо транспорту тут .


Єдине рішення працювало для мене, я не маю можливості змінити рамку Firebase відповідно до моїх потреб, що вирішило це, дякую!
Іцчак

Тепер я побачив, що Google просить NSAllowArbitraryLoads = ТАК для Admob (у Firebase). firebase.google.com/docs/admob/ios/ios9
Іцхак

6

Обхід категорії, розміщений Натаном де Врісом, пройде перевірку приватного API AppStore, і корисний у випадках, коли ви не маєте контролю над NSUrlConnectionоб'єктом. Один із прикладів - NSXMLParserце відкриття вказаної вами URL-адреси, але не відкриває NSURLRequestабоNSURLConnection .

В iOS 4 спосіб вирішення проблеми все ще працює, але лише на пристрої Simulator більше не посилається на allowsAnyHTTPSCertificateForHost:метод.


6

Ви повинні використовувати, NSURLConnectionDelegateщоб дозволити HTTPS-з'єднання, і з'явилися нові зворотні дзвінки з iOS8.

Застаріле:

connection:canAuthenticateAgainstProtectionSpace:
connection:didCancelAuthenticationChallenge:
connection:didReceiveAuthenticationChallenge:

Замість них вам потрібно заявити:

connectionShouldUseCredentialStorage: - Надіслано, щоб визначити, чи повинен завантажувач URL-адрес використовувати сховище даних для автентифікації з'єднання.

connection:willSendRequestForAuthenticationChallenge: - Повідомляє делегату, що з'єднання надішле запит на виклик автентифікації.

З willSendRequestForAuthenticationChallengeви можете використовувати , challengeяк ви робили з застарілими методами, наприклад:

// Trusting and not trusting connection to host: Self-signed certificate
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

Чи не могли б ви перевірити це: stackoverflow.com/q/56627757/1364053
nr5

3

Я опублікував деякий код суті (заснований на чужій роботі, який я зауважую), який дозволяє вам належним чином засвідчити автентифікацію на самогенерований сертифікат (і як отримати безкоштовний сертифікат - дивіться коментарі внизу Cocoanetics )

Мій код тут github


Чи не могли б ви перевірити це: stackoverflow.com/q/56627757/1364053
nr5

2

Якщо ви хочете продовжувати використовувати sendSynchronousRequest, я працюю в цьому рішенні:

FailCertificateDelegate *fcd=[[FailCertificateDelegate alloc] init];

NSURLConnection *c=[[NSURLConnection alloc] initWithRequest:request delegate:fcd startImmediately:NO];
[c setDelegateQueue:[[NSOperationQueue alloc] init]];
[c start];    
NSData *d=[fcd getData];

ви можете побачити його тут: Синхронне з'єднання Objective-C SSL


1

Завдяки AFNetworking я успішно користувався веб-службою https із кодом нижче,

NSString *aStrServerUrl = WS_URL;

// Initialize AFHTTPRequestOperationManager...
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];

[manager.requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
manager.securityPolicy.allowInvalidCertificates = YES; 
[manager POST:aStrServerUrl parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
{
    successBlock(operation, responseObject);

} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
    errorBlock(operation, error);
}];

1

Ви можете використовувати цей Код

-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
     if ([[challenge protectionSpace] authenticationMethod] == NSURLAuthenticationMethodServerTrust)
     {
         [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge];
     }
}

Використовуйте -connection:willSendRequestForAuthenticationChallenge:замість цих застарілих методів

Застаріле:

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace  
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.