신뢰할 수없는 인증서에 대해 NSURLConnection을 사용하여 SSL에 연결하는 방법은 무엇입니까?


300

SSL 웹 페이지에 연결하는 다음과 같은 간단한 코드가 있습니다.

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

인증서가 자체 서명 된 인증서 인 경우 오류가 발생하는 것을 제외하고 Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".어쨌든 (브라우저에서 수락을 누를 수있는 것처럼) 연결을 수락하도록 설정하거나 우회하는 방법이 있습니까?

답변:


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
고마워요, 완벽하게 작동합니다. https 사이트를 승인하려면 두 ifs를 제거하고 didReceiveAuthentificationChallenge 콜백에서 useCendential 부분 만 유지하십시오.
yonel

19
trustedHost는 무엇이며, 여기서 n은 객체가 어떻게 정의됩니까
Ameya

7
Ameya, NSString 객체의 NSArray가됩니다. 문자열은 @ "google.com"과 같은 호스트 이름입니다.
윌리엄 데니스

19
이 코드는 잘 작동합니다. 그러나 유효한 인증서를 갖는 모든 요점은 중간자 (man-in-the-middle) 공격을 방지하는 것입니다. 따라서이 코드를 사용하면 누군가가 "신뢰할 수있는 호스트"를 스푸핑 할 수 있습니다. 여전히 SSL의 데이터 암호화 기능을 사용할 수 있지만 호스트 식별 유효성 검사 기능이 손실됩니다.
윌리엄 데니스

42
이러한 방법은 이제 iOS 5.0 및 Mac OS X 10.6부터 더 이상 사용되지 않습니다. -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge대신 이 방법을 사용해야합니다.
Andrew R.

36

개인 API를 사용하지 않으려는 경우 (또는 사용할 수없는 경우) ASIHTTPRequest 라는 오픈 소스 (BSD 라이센스) 라이브러리가 있으며 하위 레벨을 감싸는 래퍼를 제공합니다 CFNetwork APIs. 그들은 최근에 HTTPS connections자체 서명 또는 신뢰할 수없는 인증서를 -setValidatesSecureCertificate:API 와 함께 사용할 수있는 기능을 도입했습니다 . 전체 라이브러리를 가져 오지 않으려면 소스를 동일한 기능을 직접 구현하기위한 참조로 사용할 수 있습니다.


2
팀, 당신은 어쨌든 다른 이유로 비동기식을 사용하고 싶을 수도 있습니다 (진행률 표시 줄을 표시하는 것과 같은), 나는 가장 간단한 요청을 제외하고는 내가가는 길을 찾습니다. 아마 지금 비동기를 구현하고 나중에 번거 로움을 저장해야 할 것입니다.
윌리엄 데니스

구현에 대해서는 이것을 참조하십시오 (그러나 [r setValidatesSecureCertificate : NO]; 사용) : stackoverflow.com/questions/7657786/…
Sam Brodkin

이 주제를 다시 가져 와서 죄송합니다. 그러나 iOS 5가 ARC 기능을 도입 한 이래. 이 작업을 지금 어떻게 할 수 있습니까?
Melvin Lai

이것을 확인해 주
시겠습니까?

33

이상적으로, iOS 애플리케이션이 신뢰할 수없는 인증서를 승인해야하는 경우는 두 가지 시나리오 만 있어야합니다.

시나리오 A : 자체 서명 된 인증서를 사용하는 테스트 환경에 연결되어 있습니다.

시나리오 B : 프록시를 HTTPS사용하여 트래픽을 MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.프록시하는 경우 프록시는 자체 서명 된 CA에서 서명 한 인증서를 프록시가 반환하므로 프록시가 HTTPS트래픽 을 캡처 할 수 있습니다.

프로덕션 호스트는 확실한 이유로 신뢰할 수없는 인증서를 사용해서는 안됩니다 .

테스트를 위해 iOS 시뮬레이터가 신뢰할 수없는 인증서를 승인하도록해야하는 경우 NSURLConnectionAPI에서 제공하는 내장 인증서 유효성 검사를 비활성화하기 위해 응용 프로그램 논리를 변경하지 않는 것이 좋습니다 . 이 논리를 제거하지 않고 응용 프로그램을 일반에 공개하면 중간자 (man-in-the-middle) 공격에 취약 할 수 있습니다.

테스트 목적으로 신뢰할 수없는 인증서를 승인하는 권장 방법은 인증서에 서명 한 인증 기관 (CA) 인증서를 iOS 시뮬레이터 또는 iOS 장치로 가져 오는 것입니다. iOS 시뮬레이터 에서이 작업을 수행하는 방법을 보여주는 빠른 블로그 게시물을 작성했습니다.

iOS 시뮬레이터를 사용하여 신뢰할 수없는 인증서 수락


1
멋진 물건 남자. 신뢰할 수없는 인증서를 수락하기 위해이 특별한 앱 로직을 비활성화하는 것을 잊어 버리는 것이 너무 쉽다는 것에 동의합니다.
Tomasz

"iOS 응용 프로그램이 신뢰할 수없는 인증서를 수락해야하는 경우는 두 가지 시나리오 만 있어야합니다." -certifcate를 고정 할 때 '청구 된'좋은 certifcate를 거부하는 것은 어떻습니까? 협의 : Dignotar (pwn'd) 및 Trustwave (MitM fame).
jww

코드 제거를 잊어 버린 것에 대한 진술에 전적으로 동의하십시오. 아이러니 한 점은 시뮬레이터가 자체 서명 인증서를 수락하도록하는 것보다 코드에서 이러한 변경을 수행하는 것이 훨씬 쉽다는 것입니다.
devios1

12

NSURLRequest라는 개인 메소드가 있으며 setAllowsAnyHTTPSCertificate:forHost:원하는 것을 정확하게 수행합니다. 범주 allowsAnyHTTPSCertificateForHost:NSURLRequest통해 메서드를 정의하고 YES재정의하려는 호스트에 대해 반환하도록 설정할 수 있습니다.


문서화되지 않은 API에 대한 일반적인 경고가 적용되지만 가능하다는 것을 알고는 좋습니다.
Stephen Darlington

네 개인 API를 사용하지 않는 다른 답변을 추가했습니다.
Nathan de Vries

"NSURLConnection sendSynchronousRequest :"를 사용할 때 작동합니까?
Tim Büthe

11

허용 된 답변을 보완하기 위해 훨씬 나은 보안을 위해 서버 인증서 또는 자체 루트 CA 인증서를 키 체인 ( https : //.com/a/9941559/1432048 )에 추가 할 수 있지만 NSURLConnection만으로는 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

나는 이것에 대해 어떤 신용도 취할 수 없지만, 내가 찾은 것은 내 요구에 정말 효과적이었습니다. shouldAllowSelfSignedCertBOOL변수입니다. 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 이상 및 OS X 10.11 이상에서 새로운 App Transport Security 기능 의 기본 동작입니다 .

당신은이 동작을 무시할 수 있습니다 Info.plist설정하여, NSAllowsArbitraryLoadsYES에서 NSAppTransportSecurity사전. 그러나 테스트 목적으로 만이 설정을 재정의하는 것이 좋습니다.

여기에 이미지 설명을 입력하십시오

자세한 내용은 여기에서 App Transport Technote를 참조 하십시오 .


유일한 해결책은 나를 위해 일했습니다 .Firebase 프레임 워크를 내 요구에 맞게 변경할 수있는 방법이 없어서 고맙습니다!
Yitzchak

이제 Google에서 Admob (Firebase)에 NSAllowArbitraryLoads = YES를 요청하는 것을 보았습니다. firebase.google.com/docs/admob/ios/ios9
Yitzchak

6

Nathan de Vries가 게시 한 범주 해결 방법은 AppStore 개인 API 검사를 통과하며 NSUrlConnection개체를 제어 할 수없는 경우에 유용 합니다. 하나의 예는 NSXMLParser제공 한 URL을 열지 만 NSURLRequest또는을 노출 시키지는 않습니다 NSURLConnection.

iOS 4에서는 해결 방법이 여전히 작동하는 것처럼 보이지만 장치에서만 시뮬레이터가 allowsAnyHTTPSCertificateForHost:더 이상 메서드를 호출하지 않습니다 .


6

NSURLConnectionDelegateHTTPS 연결을 허용 해야 하며 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];

이것을 확인해 주
시겠습니까?


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

당신이 여기 볼 수 있습니다 : 오브젝티브 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.