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:必要に応じてユーザーにダイアログボックスを表示した後など、後で(かなり)チャレンジメッセージにメッセージを送信できることに注意してください。


31
おかげで、それは完全に動作します。httpsサイトを受け入れる場合は、2つのifを削除し、didReceiveAuthentificationChallengeコールバックのuseCendential部分のみを保持してください。
yonel

19
trustedHostsとは何か、nはオブジェクトをどのように定義するか
Ameya

7
アメヤ、それはNSStringオブジェクトのNSArrayでしょう。文字列は、@ "google.com"のようなホスト名です。
William Denniss、

19
このコードはうまく機能します。ただし、有効な証明書を持つことの要点は、中間者攻撃を防ぐことです。したがって、このコードを使用する場合、誰かがいわゆる「信頼できるホスト」になりすます可能性があることに注意してください。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
ティム、とにかく他の理由(プログレスバーを表示できるなど)のために非同期を使用したいと思うかもしれませんが、最も単純なリクエストを除いて、私はすべての方法を見つけています。だから、多分あなたはただ今非同期を実装し、後で面倒を保存するべきです。
William Denniss、

実装のためにこれを参照してください(ただし、使用[R setValidatesSecureCertificate:NO];):stackoverflow.com/questions/7657786/...
サムBrodkin

このトピックを復活させて申し訳ありません。しかし、iOS 5からARC機能が導入されました。どうすれば今、これを機能させることができますか?
Melvin Lai、

これを確認してください:stackoverflow.com/q/56627757/1364053
nr5

33

理想的には、iOSアプリケーションが信頼されていない証明書を受け入れる必要があるのは2つのシナリオのみである必要があります。

シナリオA:自己署名証明書を使用しているテスト環境に接続しています。

シナリオB:プロキシHTTPSを使用してトラフィックをプロキシしている場合、MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.プロキシは自己署名CAによって署名された証明書を返すため、プロキシはHTTPSトラフィックます。

明らかな理由により、本番ホストは信頼できない証明書を使用してはなりません

iOSシミュレーターがテスト目的で信頼できない証明書を受け入れるようにする必要がある場合は、NSURLConnectionAPI によって提供される組み込みの証明書検証を無効にするために、アプリケーションロジックを変更しないことを強くお勧めします。このロジックを削除せずにアプリケーションを公開すると、中間者攻撃の影響を受けやすくなります。

テスト目的で信頼できない証明書を受け入れる推奨される方法は、証明書に署名した認証局(CA)証明書をiOSシミュレーターまたはiOSデバイスにインポートすることです。私はこれを行う方法を示す簡単なブログ投稿を書きました:iOSシミュレーター:

iOSシミュレータを使用して信頼できない証明書を受け入れる


1
素晴らしいもの。私は同意します。信頼されていない証明書を受け入れるためにこの特別なアプリロジックを無効にすることを忘れるのはとても簡単です。
Tomasz

「理想的には、iOSアプリケーションが信頼されていない証明書を受け入れる必要があるのは2つのシナリオのみであるべきです。」-証​​明書をピン留めするときに、「要求された」適切な証明書を拒否してはどうですか。会議:Dignotar(pwn'd)およびTrustwave(MitM fame)。
jww

コードの削除を忘れたことについての声明に完全に同意します。皮肉なことに、シミュレータに自己署名証明書を受け入れさせるよりも、コードにこの変更を加える方がはるかに簡単です。
devios1 2013年

12

NSURLRequestにはと呼ばれるプライベートメソッドsetAllowsAnyHTTPSCertificate:forHost:があります。カテゴリallowsAnyHTTPSCertificateForHost:NSURLRequest介してメソッドを定義し、YESオーバーライドするホストに戻るように設定できます。


文書化されていないAPIに関する通常の警告が適用されます...しかし、それが可能であることを知っておくとよいでしょう。
スティーブンダーリントン

ええ、絶対に。プライベートAPIの使用を含まない別の回答を追加しました。
Nathan de Vries、

「NSURLConnection sendSynchronousRequest:」を使用すると機能しますか?
TimBüthe

11

受け入れられた回答を補完するために、より優れたセキュリティのために、サーバー証明書または独自のルートCA証明書をキーチェーン(https://stackoverflow.com/a/9941559/1432048)に追加できますが、これだけではNSURLConnectionは作成されません自己署名サーバーを自動的に認証します。以下のコードをNSURLConnectionデリゲートに追加する必要があります。それはAppleサンプルコードAdvancedURLConnectionsからコピーされ、Appleサンプルコードからプロジェクトに2つのファイル(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以降およびOS X 10.11以降の新しいApp Transport Security機能のデフォルトの動作です。

あなたには、この動作をオーバーライドすることができますInfo.plist設定することで、NSAllowsArbitraryLoadsYESしてNSAppTransportSecurity辞書。ただし、この設定はテスト目的でのみ上書きすることをお勧めします。

ここに画像の説明を入力してください

詳しくはアプリの交通技術情報を参照してくださいここに


唯一の解決策は私にとってうまくいきました、私はFirebaseフレームワークを私のニーズに合わせて変更する方法がありません、それを解決しました、ありがとう!
Yitzchak

これで、GoogleがAdmob(Firebase内)に対してNSAllowArbitraryLoads = YESを要求することがわかりました。firebase.google.com/docs/admob/ios/ios9
Yitzchak

6

Nathan de Vriesによって投稿されたカテゴリの回避策は、AppStoreプライベートAPIチェックに合格するため、NSUrlConnectionオブジェクトを制御できない場合に役立ちます。1つの例は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];

これを確認してください: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 Webサービスを消費しています

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.