Recently our app went through a series of Mobile App Penetration Test (MAPT), and was flagged with bypassed SSL Pinning (https://cwe.mitre.org/data/definitions/693.html).
The tester is using Frida and is able to attach to SSL_CTX_set_custom_verify() from libboringssl.dylib, as shown in this script (https://codeshare.frida.re/@federicodotta/ios13-pinning-bypass/).
As per my research, though I'm not absolutely sure, I see that boringSSL was added since iOS 11 (https://developer.apple.com/forums/thread/88387) and (https://github.com/firebase/firebase-ios-sdk/issues/314).
I would like to check if there is anyway around this, as I am using TrustKit (https://cocoapods.org/pods/TrustKit), and I realised many other pods also tag on SSL_CTX_set_custom_verify() for SSL Pinning.
As our app requires SSL Pinning, and a resolution to this issue, I would like to ask if there is any solution, whether it being a recommended pod/library, or a native solution (preferred) to do SSL Certificate Pinning.
Thank you.
So, your goal is to increase security. Thus, you have one simple obvious path forward, after which things get more complex.
If you’re using an API that’s subject to App Transport security — that means URLSession
and everything layered on top of it, most notably the web views — then you can apply pinning declaratively using the NSPinnedDomains
property.
If that doesn’t work for you, post more details about what API you’re using and I can offer more specific advice.
Coming back to SSL_CTX_set_custom_verify
, you are correct that Apple’s TLS implementation is currently based on an internal version of BoringSSL. However, that is an implementation detail; it’s not exposed as API. Rather, Apple APIs have their own mechanism for customising TLS server trust evaluation. For example, URLSession
uses the authentication challenge mechanism.
Given that, you should not be linking to SSL_CTX_set_custom_verify
yourself. And that speaks to the mechanism used by this security tool:
-
If the tool is based on static analysis, you should look at your code to see why it’s referencing
SSL_CTX_set_custom_verify
. -
If the tool is based on dynamic analysis, then it’ll have to be smarter about how it treats
SSL_CTX_set_custom_verify
. For example, the following code, which doesn’t override TLS server trust evaluation, triggers a call to that routine on my Mac:
import Foundation
import Network
func main() {
print("connection will start")
let connection = NWConnection(to: .hostPort(host: "example.com", port: 443), using: .tls)
connection.stateUpdateHandler = { newState in
print("connection did change state, new: \(newState)")
}
connection.start(queue: .main)
print("connection did start")
withExtendedLifetime(connection) {
dispatchMain()
}
}
main()
That’s because, internally, Network framework uses Apple’s BoringSSL and it always calls SSL_CTX_set_custom_verify
in order to apply Apple’s security policies.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"