Smarter ideas worth writing about.

iOS Application Wide Bypass of SSL Certificate Chain Trust Validation

 

I recently ran into an issue when using a third party SDK where I needed to sniff the traffic between my emulator and the service. I generally use Charles WebDebugging Proxy for this, but the traffic was SSL encrypted. I didn't have source readily available for the SDK, nor did I particularly want to hunt down every NSURLConnection and implement the delegate for allowing non-verified SSL certificates. Adding the delegate to every connection also adds the responsibility of remembering to restore the SSL trust validation before any production build. Enter NSURLConnection+Debug. This utility class uses a technique known as "Swizzling" which is runtime method manipulation. Now including the class in your target and setting the DEBUGABLE pound define to true will allow any NSURLConnection that is initialized using the initWithRequest:delegate: selector to accept any and all SSL certificates, including your own MitM cert. Early on in NSURLConnection class's lifecycle the implementation for initWithRequest:delegate: is switched with the one we've defined using the "Swizzling" technique. Then when initWithRequest:delegate: is actually called in your application's code we inspect the delegate to see if it implements the needed method to allow untrusted certificates. If it does we swap it with our own, and if it does not we add the implementation to the class. Apple's app store guidelines will likely not accept an app with any "Swizzling", so be sure to remove the class before any submission. Besides, we shouldn't be accepting non-valid SSL certs in a production ready app anyway. Here's the header and implementation:

 

//
//  NSURLConnection+Debug.h
//
//  Created by Rusty Weneck on 3/15/13.
//  Copyright (c) 2013 Cardinal Solutions. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSURLConnection (Debug)

@end

 

//
//  NSURLConnection+Debug.m
//
//  Created by Rusty Weneck on 3/15/13.
//  Copyright (c) 2013 Cardinal Solutions. All rights reserved.
//

#import <objc/runtime.h>
#import "NSURLConnection+Debug.h"

#define DEBUGABLE true //If true, disable ALL SSL trust chain checks for the application.

@implementation NSURLConnection (Debug)

/**
 *  iOS 6 delegate signature
 */
-(void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{
    [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
}

-(id)swizzled_initWithRequest:(NSURLRequest *)request delegate:(id)delegate{
    if (DEBUGABLE){
        //Double Swizzle, set the method on the delegate to our instance
        Method original;

        original = class_getInstanceMethod([delegate class], @selector(connection:willSendRequestForAuthenticationChallenge:));

        if (original){
            // Get the "- (id)swizzled_initWithFrame:" method.
            Method swizzle = class_getInstanceMethod([self class], @selector(connection:willSendRequestForAuthenticationChallenge:));
            // Swap their implementations.
            method_exchangeImplementations(original, swizzle);
        }else{
            // Get the "- (id)swizzled_initWithFrame:" method.
            IMP swizzle = [self methodForSelector:@selector(connection:willSendRequestForAuthenticationChallenge:)];
            // Add the implementation to the class
            class_addMethod([delegate class], @selector(connection:willSendRequestForAuthenticationChallenge:), swizzle, "B@:@@");
        }
    }
    // This is the confusing part we need to call the original init method, but we've switched its implementation with the swizzled one.
    id result = [self swizzled_initWithRequest:request delegate:delegate];
    return result;
}

+ (void)load
{
    // The "+ load" method is called once, very early in the application life-cycle.
    // It's called even before the "main" function is called. Beware: there's no
    // autorelease pool at this point, so avoid Objective-C calls.
    Method original, swizzle;

    // Get the "- (BOOL)sqizzled_connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace" method.
    original = class_getInstanceMethod(self, @selector(initWithRequest:delegate:));
    // Get the "- (id)swizzled_initWithFrame:" method.
    swizzle = class_getInstanceMethod(self, @selector(swizzled_initWithRequest:delegate:));
    // Swap their implementations.
    method_exchangeImplementations(original, swizzle);
}

@end

 

Full source available at Github https://github.com/CardinalNow/NSURLConnection-Debug

Share:

About The Author

Consultant
Rusty is an Enterprise Java consultant and Scrum Master who is passionate about enterprise mobile development.