Làm cách nào để sử dụng NSURLCconnectection để kết nối với SSL cho chứng chỉ không đáng tin cậy?


300

Tôi có mã đơn giản sau để kết nối với trang web SSL

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

Ngoại trừ nó có lỗi nếu chứng chỉ là tự ký Error Domain=NSURLErrorDomain Code=-1202 UserInfo=0xd29930 "untrusted server certificate".Có cách nào để thiết lập nó chấp nhận kết nối không (giống như trong trình duyệt bạn có thể nhấn chấp nhận) hoặc cách bỏ qua nó?

Câu trả lời:


415

Có một API được hỗ trợ để thực hiện điều này! Thêm một cái gì đó như thế này cho NSURLConnectionđại biểu của bạn :

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

Lưu ý rằng connection:didReceiveAuthenticationChallenge:có thể gửi tin nhắn của mình đến thử thách. Gửi lại (nhiều) sau, sau khi trình bày hộp thoại cho người dùng nếu cần, v.v.


31
Cảm ơn rất nhiều, nó hoạt động hoàn hảo. Chỉ cần xóa hai if và chỉ giữ phần useCendential trong cuộc gọi lại didReceiveAuthentificationChallenge nếu bạn muốn chấp nhận bất kỳ trang web https nào.
yonel

19
Tin cậy là gì, trong đó đối tượng được xác định như thế nào
Ameya

7
Ameya, nó sẽ là một NSArray của các đối tượng NSString. Các chuỗi là tên máy chủ như @ "google.com".
William Denniss

19
Mã này hoạt động tốt. Nhưng lưu ý rằng toàn bộ quan điểm của việc có các chứng chỉ hợp lệ là để ngăn chặn các cuộc tấn công trung gian. Vì vậy, hãy lưu ý nếu bạn sử dụng mã này, ai đó có thể giả mạo cái gọi là "máy chủ đáng tin cậy". Bạn vẫn nhận được các tính năng mã hóa dữ liệu của SSL nhưng bạn mất máy chủ xác định tính năng xác thực.
William Denniss

42
Các phương pháp này hiện được coi là không dùng nữa kể từ iOS 5.0 và Mac OS X 10.6. Các -(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challengephương pháp nên được sử dụng để thay thế.
Andrew R.

36

Nếu bạn không muốn (hoặc không thể) sử dụng API riêng, có thư viện mã nguồn mở (giấy phép BSD) có tên ASIHTTPRequest cung cấp trình bao bọc xung quanh cấp thấp hơn CFNetwork APIs. Gần đây, họ đã giới thiệu khả năng cho phép HTTPS connectionssử dụng các chứng chỉ tự ký hoặc không tin cậy với -setValidatesSecureCertificate:API. Nếu bạn không muốn lấy toàn bộ thư viện, bạn có thể sử dụng nguồn làm tài liệu tham khảo để tự thực hiện chức năng tương tự.


2
Tim, bạn có thể thấy mình muốn sử dụng async vì những lý do khác (như có thể hiển thị thanh tiến trình), tôi tìm tất cả nhưng những yêu cầu đơn giản nhất đó là cách tôi đi. Vì vậy, có lẽ bạn chỉ nên triển khai Async ngay bây giờ và lưu lại rắc rối sau này.
William Denniss

Xem phần này để thực hiện (nhưng sử dụng [r setValidatesSecureCertert: NO];): stackoverflow.com/questions/7657786/ trộm
Sam Brodkin

Xin lỗi rằng tôi đã đưa chủ đề này trở lại. Nhưng kể từ khi iOS 5 giới thiệu các tính năng ARC. Làm thế nào tôi có thể làm cho công việc này bây giờ?
Melvin Lai

Bạn có thể vui lòng kiểm tra điều này không: stackoverflow.com/q/56627757/1364053
nr5

33

Lý tưởng nhất, chỉ nên có hai kịch bản khi một ứng dụng iOS cần chấp nhận chứng chỉ không đáng tin cậy.

Kịch bản A: Bạn được kết nối với môi trường thử nghiệm đang sử dụng chứng chỉ tự ký.

Kịch bản B: Bạn đang HTTPSlưu lượng truy cập MITM Proxy like Burp Suite, Fiddler, OWASP ZAP, etc.Proxy bằng cách sử dụng Proxy sẽ trả lại chứng chỉ được ký bởi CA tự ký để proxy có thể nắm bắt HTTPSlưu lượng.

Máy chủ sản xuất không bao giờ nên sử dụng chứng chỉ không đáng tin cậy vì lý do rõ ràng .

Nếu bạn cần phải có trình giả lập iOS chấp nhận chứng chỉ không đáng tin cậy cho mục đích thử nghiệm, bạn không nên thay đổi logic ứng dụng để vô hiệu hóa xác thực chứng chỉ tích hợp do NSURLConnectionAPI cung cấp . Nếu ứng dụng được phát hành ra công chúng mà không loại bỏ logic này, nó sẽ dễ bị tấn công giữa chừng.

Cách được đề xuất để chấp nhận chứng chỉ không đáng tin cậy cho mục đích thử nghiệm là nhập chứng chỉ Tổ chức phát hành chứng chỉ (CA) đã ký chứng chỉ vào Trình mô phỏng iOS hoặc thiết bị iOS của bạn. Tôi đã viết một bài đăng blog nhanh trong đó trình bày cách thực hiện điều này mà Trình mô phỏng iOS tại:

chấp nhận chứng chỉ không tin cậy bằng trình giả lập ios


1
Người đàn ông thứ tuyệt vời. Tôi đồng ý, thật dễ dàng để quên việc vô hiệu hóa logic ứng dụng đặc biệt này để chấp nhận bất kỳ chứng chỉ không đáng tin cậy nào.
Tomasz

"Lý tưởng nhất, chỉ nên có hai kịch bản khi một ứng dụng iOS cần chấp nhận chứng chỉ không đáng tin cậy." - Làm thế nào về việc từ chối chứng nhận tốt 'được yêu cầu' khi ghim chứng nhận? Confer: Dignotar (pwn'd) và Trustwave (danh tiếng MitM).
jww

Hoàn toàn đồng ý với tuyên bố của bạn về việc quên xóa mã. Điều trớ trêu là việc thực hiện thay đổi mã này dễ dàng hơn nhiều so với việc trình giả lập chấp nhận các certs tự ký.
devios1

12

NSURLRequestcó một phương thức riêng được gọi setAllowsAnyHTTPSCertificate:forHost:, phương thức này sẽ thực hiện chính xác những gì bạn muốn. Bạn có thể xác định allowsAnyHTTPSCertificateForHost:phương thức trên NSURLRequestthông qua một danh mục và đặt nó trở lại YEScho máy chủ mà bạn muốn ghi đè.


Thông báo trước về các API không có giấy tờ được áp dụng ... nhưng thật tốt khi biết rằng điều đó là có thể.
Stephen Darlington

Yeah tuyệt đối. Tôi đã thêm một câu trả lời khác không liên quan đến việc sử dụng API riêng.
Nathan de Vries

Điều đó có hoạt động khi bạn sử dụng "NSURLConnection sendSynyncousRequest:" không?
Tim Büthe

11

Để bổ sung cho câu trả lời được chấp nhận, để bảo mật tốt hơn nhiều, bạn có thể thêm chứng chỉ máy chủ hoặc chứng chỉ CA gốc của mình vào móc khóa ( https://stackoverflow.com/a/9941559/1432048 ), tuy nhiên, việc này một mình sẽ không tạo ra kết nối NSURLC tự động xác thực máy chủ tự ký của bạn. Bạn vẫn cần thêm mã dưới đây vào đại biểu NSURLConnection của mình, mã này được sao chép từ mã mẫu AdvancedURLConnections của Apple và bạn cần thêm hai tệp (Credentials.h, Credentials.m) từ mã mẫu táo vào các dự án của bạn.

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

Tôi không thể nhận bất kỳ khoản tín dụng nào cho việc này, nhưng khoản này tôi thấy hoạt động thực sự tốt cho nhu cầu của mình. shouldAllowSelfSignedCertBOOLbiến của tôi Chỉ cần thêm vào NSURLConnectionđại biểu của bạn và bạn sẽ được rockin để bỏ qua nhanh trên cơ sở kết nối.

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

Trong iOS 9, các kết nối SSL sẽ thất bại đối với tất cả các chứng chỉ không hợp lệ hoặc tự ký. Đây là hành vi mặc định của tính năng Bảo mật Vận chuyển Ứng dụng mới trong iOS 9.0 trở lên và trên OS X 10.11 trở lên.

Bạn có thể ghi đè hành vi này trong Info.plist, bằng cách đặt NSAllowsArbitraryLoadsthành YEStrong NSAppTransportSecuritytừ điển. Tuy nhiên, tôi khuyên bạn nên ghi đè cài đặt này chỉ cho mục đích thử nghiệm.

nhập mô tả hình ảnh ở đây

Để biết thông tin, xem App Transport Technote tại đây .


Giải pháp duy nhất hiệu quả với tôi, tôi không có cách nào thay đổi khung Firebase để phù hợp với nhu cầu của mình, điều đó đã giải quyết được, cảm ơn!
Yitzchak

Bây giờ tôi đã thấy rằng Google yêu cầu NS ALLowArbitraryLoads = CÓ cho admob (trong Firebase). firebase.google.com/docs/admob/ios/ios9
Yitzchak

6

Cách giải quyết danh mục được đăng bởi Nathan de Vries sẽ vượt qua kiểm tra API riêng của AppStore và rất hữu ích trong trường hợp bạn không có quyền kiểm soát NSUrlConnectionđối tượng. Một ví dụ là NSXMLParsersẽ mở URL bạn cung cấp, nhưng không để lộ NSURLRequesthoặc NSURLConnection.

Trong iOS 4, cách giải quyết có vẻ vẫn hoạt động, nhưng chỉ trên thiết bị, Trình mô phỏng không gọi allowsAnyHTTPSCertificateForHost:phương thức này nữa.


6

Bạn phải sử dụng NSURLConnectionDelegateđể cho phép kết nối HTTPS và có các cuộc gọi lại mới với iOS8.

Không dùng nữa

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

Thay vào đó, bạn cần khai báo:

connectionShouldUseCredentialStorage: - Được gửi để xác định xem trình tải URL có nên sử dụng bộ lưu trữ thông tin xác thực để xác thực kết nối hay không.

connection:willSendRequestForAuthenticationChallenge: - Nói với đại biểu rằng kết nối sẽ gửi yêu cầu cho một thách thức xác thực.

Với willSendRequestForAuthenticationChallengebạn có thể sử dụng challengenhư bạn đã làm với các phương thức không dùng nữa, ví dụ:

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

Bạn có thể vui lòng kiểm tra điều này không: stackoverflow.com/q/56627757/1364053
nr5

3

Tôi đã đăng một số mã chính (dựa trên công việc của người khác mà tôi lưu ý) cho phép bạn xác thực chính xác với chứng chỉ tự tạo (và cách nhận chứng chỉ miễn phí - xem phần dưới cùng của Cocoanetic )

Mã của tôi ở đây github


Bạn có thể vui lòng kiểm tra điều này không: stackoverflow.com/q/56627757/1364053
nr5

2

Nếu bạn muốn tiếp tục sử dụng sendSynyncousRequest, tôi sẽ làm việc trong giải pháp này:

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

bạn có thể thấy nó ở đây: Kết nối đồng bộ SSL Objective-C


1

Với AFNetworking, tôi đã sử dụng thành công dịch vụ web https với mã bên dưới,

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

Bạn có thể sử dụng Mã này

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

Sử dụng -connection:willSendRequestForAuthenticationChallenge:thay cho các Phương pháp không dùng nữa

Không dùng nữa

-(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace  
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge 
-(void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.