Bagaimana cara menggunakan NSURLConnection untuk terhubung dengan SSL untuk sertifikat yang tidak terpercaya?

300

Saya memiliki kode sederhana berikut untuk terhubung ke halaman web SSL

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

Kecuali itu memberikan kesalahan jika sertifikat itu ditandatangani sendiri. Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".Apakah ada cara untuk mengaturnya untuk menerima koneksi (seperti di browser Anda dapat menekan accept) atau cara untuk memotongnya?

erotsppa
sumber

Jawaban:

415

Ada API yang didukung untuk mencapai ini! Tambahkan sesuatu seperti ini ke NSURLConnectiondelegasi Anda :

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

Catatan yang connection:didReceiveAuthenticationChallenge:dapat mengirim pesannya ke challenge.sender nanti, setelah memberikan kotak dialog kepada pengguna jika perlu, dll.

Gordon Henriksen
sumber
31
Terima kasih banyak, ini bekerja dengan sempurna. Hapus saja kedua ifs dan simpan hanya bagian useCendential di callback didReceiveAuthentificationChallenge jika Anda ingin menerima situs https.
yonel
19
apa yang dimaksud denganhosted, di mana n bagaimana objek didefinisikan
Ameya
7
Ameya, itu akan menjadi NSArray dari objek NSString. String adalah nama host seperti @ "google.com".
William Denniss
19
Kode ini berfungsi dengan baik. Tetapi perhatikan bahwa seluruh poin dari memiliki sertifikat yang valid adalah untuk mencegah serangan man-in-the-middle. Jadi berhati-hatilah jika Anda menggunakan kode ini, seseorang dapat menipu apa yang disebut "host tepercaya". Anda masih mendapatkan fitur enkripsi data SSL tetapi Anda kehilangan fitur validasi identifikasi host.
William Denniss
42
Metode ini sekarang dianggap usang pada iOS 5.0 dan Mac OS X 10.6. The -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challengemetode harus digunakan sebagai gantinya.
Andrew R.
36

Jika Anda tidak ingin (atau tidak dapat) menggunakan API pribadi, ada pustaka sumber terbuka (lisensi BSD) yang disebut ASIHTTPRequest yang menyediakan pembungkus di sekitar level yang lebih rendah CFNetwork APIs. Mereka baru-baru ini memperkenalkan kemampuan untuk mengizinkan HTTPS connectionsmenggunakan sertifikat yang ditandatangani sendiri atau tidak dipercaya dengan -setValidatesSecureCertificate:API. Jika Anda tidak ingin menarik seluruh pustaka, Anda bisa menggunakan sumber itu sebagai referensi untuk mengimplementasikan fungsi yang sama sendiri.

Nathan de Vries
sumber
2
Tim, Anda mungkin mendapati diri Anda ingin menggunakan async karena alasan lain (seperti dapat menampilkan bilah kemajuan), saya menemukan untuk semua kecuali permintaan yang paling sederhana yang saya lakukan. Jadi mungkin Anda harus menerapkan Async sekarang dan menyimpan kerumitan nanti.
William Denniss
Lihat ini untuk implementasinya (tetapi gunakan [r setValidatesSecureCertificate: NO];): stackoverflow.com/questions/7657786/…
Sam Brodkin
Maaf saya telah mengangkat topik ini. Namun sejak iOS 5 memperkenalkan fitur ARC. Bagaimana saya bisa membuat ini berfungsi sekarang?
Melvin Lai
Bisakah Anda periksa ini: stackoverflow.com/q/56627757/1364053
nr5
33

Idealnya, seharusnya hanya ada dua skenario kapan aplikasi iOS perlu menerima sertifikat yang tidak dipercaya.

Skenario A: Anda terhubung ke lingkungan pengujian yang menggunakan sertifikat yang ditandatangani sendiri.

Skenario B: Anda sedang Proxy HTTPStrafik menggunakan MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.The Proxies akan mengembalikan sertifikat yang ditandatangani oleh CA yang ditandatangani sendiri sehingga proxy dapat menangkap HTTPStraffic.

Host produksi tidak boleh menggunakan sertifikat yang tidak dipercaya untuk alasan yang jelas .

Jika Anda perlu agar simulator iOS menerima sertifikat yang tidak tepercaya untuk tujuan pengujian, sangat disarankan agar Anda tidak mengubah logika aplikasi agar menonaktifkan validasi sertifikat bawaan yang disediakan oleh NSURLConnectionAPI. Jika aplikasi ini dirilis ke publik tanpa menghapus logika ini, itu akan rentan terhadap serangan man-in-the-middle.

Cara yang disarankan untuk menerima sertifikat yang tidak tepercaya untuk tujuan pengujian adalah dengan mengimpor sertifikat Certificate Authority (CA) yang menandatangani sertifikat ke iOS Simulator atau perangkat iOS Anda. Saya menulis posting blog cepat yang menunjukkan bagaimana melakukan ini di mana iOS Simulator di:

menerima sertifikat yang tidak dipercaya menggunakan simulator ios

pengguna890103
sumber
1
Pria yang luar biasa. Saya setuju, sangat mudah untuk melupakan menonaktifkan logika aplikasi khusus ini untuk menerima sertifikat yang tidak dipercaya.
Tomasz
"Idealnya, seharusnya hanya ada dua skenario kapan aplikasi iOS perlu menerima sertifikat yang tidak dipercaya." - Bagaimana dengan menolak sertifikat yang 'diklaim' bagus saat menyematkan sertifikat? Berunding: Dignotar (pwn'd) dan Trustwave (ketenaran MitM).
jww
Setuju sepenuhnya dengan pernyataan Anda tentang lupa menghapus kode. Ironinya adalah bahwa lebih mudah untuk membuat perubahan dalam kode ini daripada meminta simulator untuk menerima sertifikat yang ditandatangani sendiri.
devios1
12

NSURLRequestmemiliki metode pribadi yang disebut setAllowsAnyHTTPSCertificate:forHost:, yang akan melakukan apa yang Anda inginkan. Anda bisa mendefinisikan allowsAnyHTTPSCertificateForHost:metode pada NSURLRequestmelalui kategori, dan mengaturnya untuk kembali YESuntuk host yang ingin Anda timpa.

Nathan de Vries
sumber
Peringatan biasa tentang API tidak berdokumen berlaku ... tapi senang mengetahui bahwa itu mungkin.
Stephen Darlington
Ya tentu saja. Saya telah menambahkan jawaban lain yang tidak melibatkan penggunaan API pribadi.
Nathan de Vries
Apakah itu berfungsi saat Anda menggunakan "NSURLConnection sendSynchronousRequest:"?
Tim Büthe
11

Untuk melengkapi jawaban yang diterima, untuk keamanan yang jauh lebih baik, Anda dapat menambahkan sertifikat server atau sertifikat CA root Anda sendiri ke gantungan kunci ( https://stackoverflow.com/a/9941559/1432048 ), namun melakukan hal ini sendirian tidak akan membuat NSURLConnection otentikasi server yang Anda tandatangani sendiri secara otomatis. Anda masih perlu menambahkan kode di bawah ini ke delegasi NSURLConnection Anda, disalin dari kode sampel Apple AdvancedURLConnections , dan Anda perlu menambahkan dua file (Credentials.h, Credentials.m) dari kode sampel apple ke proyek Anda.

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

Saya tidak dapat mengambil kredit untuk ini, tetapi yang saya temukan ini bekerja dengan sangat baik untuk kebutuhan saya. shouldAllowSelfSignedCertadalah BOOLvariabel saya . Cukup tambahkan ke NSURLConnectiondelegasi Anda dan Anda harus rockin untuk memotong cepat berdasarkan basis koneksi.

- (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;
}
Ryna
sumber
10

Di iOS 9, koneksi SSL akan gagal untuk semua sertifikat yang tidak sah atau ditandatangani sendiri. Ini adalah perilaku default fitur Keamanan Transportasi Aplikasi baru di iOS 9.0 atau lebih baru, dan pada OS X 10.11 dan yang lebih baru.

Anda dapat mengganti perilaku ini di Info.plist, dengan mengatur NSAllowsArbitraryLoadske YESdalam NSAppTransportSecuritykamus. Namun, saya sarankan mengganti pengaturan ini untuk tujuan pengujian saja.

masukkan deskripsi gambar di sini

Untuk informasi, lihat App Transport Technote di sini .

Johnnieb
sumber
Satu-satunya solusi yang bekerja untuk saya, saya tidak punya cara untuk mengubah kerangka Firebase untuk menyesuaikan kebutuhan saya, yang menyelesaikannya, terima kasih!
Yitzchak
Sekarang saya melihat bahwa Google meminta NSAllowArbitraryLoads = YA untuk Admob (di Firebase). firebase.google.com/docs/admob/ios/ios9
Yitzchak
6

Pemecahan masalah kategori yang diposting oleh Nathan de Vries akan melewati pemeriksaan API AppStore pribadi, dan berguna dalam kasus di mana Anda tidak memiliki kendali atas NSUrlConnectionobjek. Salah satu contoh adalah NSXMLParseryang akan membuka URL yang Anda berikan, tetapi tidak mengekspos NSURLRequestatau NSURLConnection.

Di iOS 4 solusinya tampaknya masih berfungsi, tetapi hanya pada perangkat, Simulator tidak lagi menggunakan allowsAnyHTTPSCertificateForHost:metode ini.

Alex Suzuki
sumber
6

Anda harus menggunakan NSURLConnectionDelegateuntuk mengizinkan koneksi HTTPS dan ada panggilan balik baru dengan iOS8.

Usang:

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

Alih-alih itu, Anda perlu mendeklarasikan:

connectionShouldUseCredentialStorage: - Dikirim untuk menentukan apakah pemuat URL harus menggunakan penyimpanan kredensial untuk mengautentikasi koneksi.

connection:willSendRequestForAuthenticationChallenge: - Memberitahu delegasi bahwa koneksi akan mengirim permintaan untuk tantangan otentikasi.

Dengan willSendRequestForAuthenticationChallengeAnda dapat menggunakan challengeseperti yang Anda lakukan dengan metode yang sudah usang, misalnya:

// Trusting and not trusting connection to host: Self-signed certificate
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
ricardopereira
sumber
Bisakah Anda periksa ini: stackoverflow.com/q/56627757/1364053
nr5
3

Saya memposting beberapa kode inti (berdasarkan pada karya orang lain yang saya catat) yang memungkinkan Anda mengautentikasi dengan benar terhadap sertifikat yang dihasilkan sendiri (dan cara mendapatkan sertifikat gratis - lihat komentar di bawah Cocoanetics )

Kode saya di sini github

David H
sumber
Bisakah Anda periksa ini: stackoverflow.com/q/56627757/1364053
nr5
2

Jika Anda ingin tetap menggunakan sendSynchronousRequest saya bekerja dalam solusi ini:

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

Anda dapat melihatnya di sini: Objective-C SSL Synchronous Connection

jgorozco
sumber
1

Dengan AFNetworking, saya berhasil menggunakan layanan web https dengan kode di bawah ini,

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);
}];
cjd
sumber
1

Anda dapat menggunakan Kode ini

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

Gunakan -connection:willSendRequestForAuthenticationChallenge:sebagai ganti dari Metode-Metode Kuno ini

Usang:

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace  
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Vaibhav Sharma
sumber