App Transport Security (ATS)

App Transport Security (ATS)

ATS enforces secure network connections, but proper configuration is essential for both security and functionality.

ATS Configuration Best Practices:

<!-- Info.plist - Secure ATS configuration -->
<key>NSAppTransportSecurity</key>
<dict>
    <!-- Enable ATS globally -->
    <key>NSAllowsArbitraryLoads</key>
    <false/>
    
    <!-- Configure exceptions only when absolutely necessary -->
    <key>NSExceptionDomains</key>
    <dict>
        <key>legacy-api.example.com</key>
        <dict>
            <!-- Require minimum TLS 1.2 -->
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            
            <!-- Require forward secrecy -->
            <key>NSExceptionRequiresForwardSecrecy</key>
            <true/>
            
            <!-- Allow this specific domain -->
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>

Network Security Implementation:

// URLSession with certificate pinning
class SecureNetworkManager: NSObject {
    private lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.default
        configuration.tlsMinimumSupportedProtocolVersion = .TLSv12
        configuration.tlsMaximumSupportedProtocolVersion = .TLSv13
        
        return URLSession(configuration: configuration,
                         delegate: self,
                         delegateQueue: nil)
    }()
    
    func performSecureRequest(to url: URL, completion: @escaping (Data?, Error?) -> Void) {
        let task = session.dataTask(with: url) { data, response, error in
            completion(data, error)
        }
        task.resume()
    }
}

extension SecureNetworkManager: URLSessionDelegate {
    func urlSession(_ session: URLSession, 
                    didReceive challenge: URLAuthenticationChallenge, 
                    completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust else {
            completionHandler(.cancelAuthenticationChallenge, nil)
            return
        }
        
        // Implement certificate pinning
        if let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
            let serverCertificateData = SecCertificateCopyData(certificate) as Data
            let localCertificate = loadPinnedCertificate()
            
            if serverCertificateData == localCertificate {
                let credential = URLCredential(trust: serverTrust)
                completionHandler(.useCredential, credential)
            } else {
                completionHandler(.cancelAuthenticationChallenge, nil)
            }
        } else {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }
}