How do TLS and App Transport Security protect a request, and what is certificate pinning, and why is it fraught?
Predict first
A pinned leaf certificate is renewed on the server with a new key and the app has no backup pin. What do installed apps do?
// A: keep working, since the domain is the same
// B: fail every request until users install an update
// C: fall back to HTTP
- A
- B
- C
Mental model. TLS is checking the ID of the person at the door. Pinning is also checking they are on your specific guest list, which stops impostors with convincing IDs, but locks out your real guest if they change their name without telling you.
TLS gives a connection confidentiality, integrity and authentication of the server: the server presents a certificate chain, and the client verifies that it chains to a trusted root, is valid for the host name, has not expired and has not been revoked. App Transport Security (ATS) is Apple’s policy layer: by default URLSession refuses plain HTTP and requires TLS 1.2 or later with strong cipher suites and forward secrecy. You can add exceptions in Info.plist (NSAppTransportSecurity, per domain or globally), but broad exceptions are rejected or questioned in App Review and defeat the point. The validation itself is done by the system’s trust evaluation (SecTrust), and it respects the user’s and organisation’s installed roots.
Certificate pinning goes further: the app only accepts certificates (or, better, public keys) that it knows, so a certificate issued by a compromised or malicious but otherwise trusted authority, or a user-installed proxy root, is rejected. You implement it in the delegate’s urlSession(_:didReceive:completionHandler:) for server-trust challenges (compare the server’s key hash with a stored set), or declaratively with the NSPinnedDomains Info.plist key (iOS 14+), which pins by SPKI hash. It is fraught because it turns a certificate rotation into an outage: if the server’s key changes and your pinned value does not, every installed copy of the app stops connecting until users update. Mitigations: pin the public key of an intermediate or a backup key, not the leaf certificate; ship at least one backup pin that is not yet in use; keep a remote way to disable or update pins (with care, as that itself is an attack surface); monitor expiry; and test the rotation. Pinning is not a defence against a compromised device or a jailbroken one with the runtime hooked (it can be bypassed), so treat it as one layer for high-value APIs, not the whole security story. For most apps, correct TLS and ATS plus short-lived tokens are enough.
The pin no longer matches, so every connection is rejected. This is the main operational risk of pinning.
Common traps
- Pinning the leaf certificate with no backup pin. A normal renewal then breaks every installed app.
- Disabling ATS globally to “fix” a connection error. Fix the server’s TLS configuration or add a narrow exception.
They might follow up with
- What do you pin? A public key hash (of an intermediate, with a backup), not the leaf certificate.
- How do you pin declaratively?
NSPinnedDomainsin Info.plist, by SPKI hash.
Related questions
- 12.2.1 How do HTTP/1.1, HTTP/2 and HTTP/3 differ, and what does that mean for an iOS client?
- 12.6.2 How would you design a networking layer shared by many apps and teams: interceptors, mocking, and URLProtocol?
- 12.1.1 What do the URLSession configurations control, and how do default, ephemeral and background sessions differ?
← How do HTTP/1.1, HTTP/2 and HTTP/3 differ, and what does that mean…