Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions QonversionTests/APIClientTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,27 @@ - (void)testThatClientDetectBrokenData {
[self waitForExpectationsWithTimeout:keyQNTestTimeout handler:nil];
}

- (void)testThatServerTrustUsesSystemValidation {
NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc]
initWithHost:@"attacker.invalid"
port:443
protocol:NSURLProtectionSpaceHTTPS
realm:nil
authenticationMethod:NSURLAuthenticationMethodServerTrust];
id challenge = OCMClassMock([NSURLAuthenticationChallenge class]);
OCMStub([challenge protectionSpace]).andReturn(protectionSpace);

__block NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengeUseCredential;
__block NSURLCredential *credential = [NSURLCredential credentialWithUser:@"unexpected" password:@"unexpected" persistence:NSURLCredentialPersistenceNone];

[self.client URLSession:nil didReceiveChallenge:challenge completionHandler:^(NSURLSessionAuthChallengeDisposition receivedDisposition, NSURLCredential *receivedCredential) {
disposition = receivedDisposition;
credential = receivedCredential;
}];

XCTAssertEqual(disposition, NSURLSessionAuthChallengePerformDefaultHandling);
XCTAssertNil(credential);
[challenge stopMocking];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,9 @@ class NetworkProvider: NSObject, NetworkProviderInterface, URLSessionDelegate {
}
}

// MARK: - Temporary SSL bypass for staging. Remove before release.
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
} else {
completionHandler(.performDefaultHandling, nil)
}
// Keep system trust-chain and hostname validation for every endpoint.
completionHandler(.performDefaultHandling, nil)
}
}

Expand Down
11 changes: 4 additions & 7 deletions Sources/Qonversion/Qonversion/Services/QNAPIClient/QNAPIClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -678,14 +678,11 @@ - (void)storeRequestIfNeeded:(NSURLRequest *)request {
}
}

// MARK: - Temporary SSL bypass for staging. Remove before release.
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
} else {
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}
// Never manufacture a credential from an unverified SecTrust. Delegating to
// URLSession preserves the platform trust chain and hostname checks for the
// default API as well as customer-provided HTTPS proxy URLs.
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

@end
Loading