Como usar o NSURLConnection para se conectar ao SSL para um certificado não confiável?


300

Eu tenho o seguinte código simples para conectar-me a uma página SSL

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

Exceto se houver um erro se o certificado for autoassinado. Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".Existe uma maneira de configurá-lo para aceitar conexões de qualquer maneira (como em um navegador que você pode pressionar em aceitar) ou uma maneira de ignorá-lo?

Respostas:


415

Há uma API suportada para fazer isso! Adicione algo assim ao seu NSURLConnectiondelegado:

- (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];
}

Observe que connection:didReceiveAuthenticationChallenge:pode enviar sua mensagem para challenge.sender (muito) posteriormente, depois de apresentar uma caixa de diálogo ao usuário, se necessário, etc.


31
Muito obrigado, funciona perfeitamente. Apenas remova os dois ifs e mantenha apenas a parte useCendential no retorno de chamada didReceiveAuthentificationChallenge se desejar aceitar qualquer site https.
Página

19
o que é um TrustedHosts, onde n, como é o objeto definido
Ameya

7
Ameya, seria um NSArray de objetos NSString. As strings são os nomes de host como @ "google.com".
William Denniss

19
Esse código funciona bem. Mas observe que o objetivo de ter certificados válidos é impedir ataques do tipo intermediário. Portanto, esteja ciente de que, se você usar esse código, alguém poderá falsificar o chamado "host confiável". Você ainda obtém os recursos de criptografia de dados do SSL, mas perde o host para identificar os recursos de validação.
precisa

42
Agora, esses métodos são considerados obsoletos no iOS 5.0 e no Mac OS X 10.6. O -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challengemétodo deve ser usado em seu lugar.
Andrew R.

36

Se você não estiver disposto (ou não puder) a usar APIs privadas, há uma biblioteca de código aberto (licença BSD) chamada ASIHTTPRequest que fornece um wrapper em torno do nível inferior CFNetwork APIs. Eles recentemente introduziram a capacidade de permitir o HTTPS connectionsuso de certificados autoassinados ou não confiáveis ​​com a -setValidatesSecureCertificate:API. Se você não deseja extrair a biblioteca inteira, pode usar a fonte como referência para implementar a mesma funcionalidade.


2
Tim, você pode querer usar o assíncrono por outros motivos de qualquer maneira (como poder mostrar uma barra de progresso), acho que para todos, exceto os pedidos mais simples, é assim que eu vou. Então, talvez você deva implementar o Async agora e salvar os problemas mais tarde.
precisa

Veja isso para a implementação (mas use [r setValidatesSecureCertificate: NO];): stackoverflow.com/questions/7657786/…
Sam Brodkin

Desculpe por trazer esse tópico de volta. Mas desde que o iOS 5 introduziu os recursos do ARC. Como posso fazer isso funcionar agora?
Melvin Lai

Poderia, por favor verifique o seguinte: stackoverflow.com/q/56627757/1364053
NR 5

33

Idealmente, deve haver apenas dois cenários em que um aplicativo iOS precisaria aceitar um certificado não confiável.

Cenário A: Você está conectado a um ambiente de teste que está usando um certificado autoassinado.

Cenário B: você está proxy de HTTPStráfego usando um proxy MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.que retornará um certificado assinado por uma CA autoassinada para que o proxy possa capturar HTTPStráfego.

Os hosts de produção nunca devem usar certificados não confiáveis ​​por razões óbvias .

Se você precisar que o simulador do iOS aceite um certificado não confiável para fins de teste, é altamente recomendável que você não altere a lógica do aplicativo para desativar a validação de certificado interna fornecida pelas NSURLConnectionAPIs. Se o aplicativo for lançado ao público sem remover essa lógica, ele estará suscetível a ataques do tipo man-in-the-middle.

A maneira recomendada de aceitar certificados não confiáveis ​​para fins de teste é importar o certificado da Autoridade de Certificação (CA) que assinou o certificado no seu Simulador iOS ou dispositivo iOS. Eu escrevi um post rápido no blog que demonstra como fazer isso no iOS Simulator:

aceitando certificados não confiáveis ​​usando o simulador ios


1
Homem material incrível. Eu concordo, é tão fácil esquecer a desativação dessa lógica de aplicativo especial para aceitar qualquer certificado não confiável.
Tomasz

"Idealmente, deve haver apenas dois cenários de quando um aplicativo iOS precisaria aceitar um certificado não confiável". - Que tal rejeitar um bom certificado 'reivindicado' ao fixá-lo? Conferência: Dignotar (pwn'd) e Trustwave (fama do MitM).
JWW

Concordo totalmente com a sua declaração sobre o esquecimento de remover o código. A ironia é que é muito mais fácil fazer essa alteração no código do que fazer com que o simulador aceite certificados autoassinados.
Devios1 17/05

12

NSURLRequesttem um método particular chamado setAllowsAnyHTTPSCertificate:forHost:, que fará exatamente o que você deseja. Você pode definir o allowsAnyHTTPSCertificateForHost:método por NSURLRequestmeio de uma categoria e configurá-lo para retornar YESpara o host que você deseja substituir.


Advertências comuns sobre APIs não documentadas se aplicam ... mas é bom saber que é possível.
Stephen Darlington

Sim absolutamente. Adicionei outra resposta que não envolve o uso de APIs privadas.
Nathan de Vries

Isso funciona quando você usa "NSURLConnection sendSynchronousRequest:"?
Tim Büthe

11

Para complementar a resposta aceita, para uma segurança muito melhor, você pode adicionar seu certificado de servidor ou seu próprio certificado CA raiz às chaves ( https://stackoverflow.com/a/9941559/1432048 ), mas fazer isso sozinho não fará com que NSURLConnection autentique seu servidor autoassinado automaticamente. Você ainda precisa adicionar o código abaixo ao seu representante do NSURLConnection, ele é copiado do código de exemplo da Apple AdvancedURLConnections e você precisa adicionar dois arquivos (Credentials.h, Credentials.m) do código de exemplo da Apple aos seus projetos.

- (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

Não posso aceitar nenhum crédito por isso, mas este que achei funcionou muito bem para minhas necessidades. shouldAllowSelfSignedCerté minha BOOLvariável Basta adicionar ao seu NSURLConnectiondelegado e você deve estar agitado para um desvio rápido por conexão.

- (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

No iOS 9, as conexões SSL falharão para todos os certificados inválidos ou autoassinados. Esse é o comportamento padrão do novo recurso App Transport Security no iOS 9.0 ou posterior e no OS X 10.11 e posterior.

Você pode substituir esse comportamento no Info.plist, definindo NSAllowsArbitraryLoadspara YESno NSAppTransportSecuritydicionário. No entanto, recomendo substituir essa configuração apenas para fins de teste.

insira a descrição da imagem aqui

Para obter informações, consulte App Transport Technote aqui .


A única solução funcionou para mim, não tenho como alterar a estrutura do Firebase para atender às minhas necessidades, e isso resolveu, obrigado!
Yitzchak

Agora vi que o Google solicita NSAllowArbitraryLoads = YES para Admob (no Firebase). firebase.google.com/docs/admob/ios/ios9
Yitzchak

6

A solução alternativa da categoria publicada por Nathan de Vries passará nas verificações de API privadas da AppStore e é útil nos casos em que você não tem controle do NSUrlConnectionobjeto. Um exemplo é o NSXMLParserque abrirá o URL que você fornece, mas não expõe o NSURLRequestou NSURLConnection.

No iOS 4, a solução alternativa ainda parece funcionar, mas apenas no dispositivo, o Simulador não invoca mais o allowsAnyHTTPSCertificateForHost:método.


6

Você precisa usar NSURLConnectionDelegatepara permitir conexões HTTPS e há novos retornos de chamada no iOS8.

Descontinuada:

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

Em vez disso, você precisa declarar:

connectionShouldUseCredentialStorage: - Enviado para determinar se o carregador de URL deve usar o armazenamento de credenciais para autenticar a conexão.

connection:willSendRequestForAuthenticationChallenge: - Informa ao delegado que a conexão enviará uma solicitação para um desafio de autenticação.

Com willSendRequestForAuthenticationChallengevocê pode usar challengecomo você fez com os métodos preteridos, por exemplo:

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

Poderia, por favor verifique o seguinte: stackoverflow.com/q/56627757/1364053
NR 5

3

Publiquei um código essencial (com base no trabalho de outra pessoa que observo) que permite que você se autentique adequadamente em um certificado auto-gerado (e como obter um certificado gratuito - veja os comentários na parte inferior do Cocoanetics )

Meu código está aqui github


Poderia, por favor verifique o seguinte: stackoverflow.com/q/56627757/1364053
NR 5

2

Se você quiser continuar usando o sendSynchronousRequest, eu trabalho nesta solução:

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];

Você pode vê-lo aqui: Conexão síncrona SSL do Objective-C


1

Com o AFNetworking , consumi com sucesso o https webservice com o código abaixo,

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

Você pode usar este código

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

Use em -connection:willSendRequestForAuthenticationChallenge:vez desses métodos preteridos

Descontinuada:

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace  
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.