VNX-SWIFT-004 – Swift TLS Certificate Validation Disabled in URLSession or Alamofire
Overview
This rule detects three distinct code patterns in Swift files that each indicate TLS certificate validation has been bypassed:
- Use of
URLCredential(trust:)— constructing a credential that unconditionally trusts the server’s certificate chain. - Calling
completionHandler(.useCredential, ...)inside adidReceive challengedelegate method without inspectingserverTrust— accepting a credential in an authentication challenge without performing certificate evaluation. - Setting
disableEvaluation,DisableTrustEvaluation, or.disabledwithin a block that referencesServerTrustPolicy,ServerTrustEvaluating, orTrustEvaluation— disabling Alamofire’s server trust evaluation.
All three patterns result in the application accepting any certificate presented by the server, including self-signed, expired, and attacker-controlled certificates.
This maps to CWE-295: Improper Certificate Validation.
Severity: High | CWE: CWE-295 – Improper Certificate Validation
Why This Matters
Disabling TLS certificate validation eliminates the only mechanism that proves the server the application is communicating with is who it claims to be. On any untrusted network — public Wi-Fi, hotel networks, carrier networks with traffic inspection appliances — an attacker can perform a man-in-the-middle (MITM) attack by presenting their own certificate to the client. With validation disabled, the client accepts this certificate and the attacker receives all traffic in cleartext: credentials, session tokens, payment data, health information, and any other sensitive content.
This pattern is extremely common in development codebases. Developers disable validation to work with self-signed certificates on local test servers and forget to re-enable it before shipping. Tools such as Burp Suite and Charles Proxy are explicitly designed to exploit this class of vulnerability to intercept mobile app traffic, and penetration testers routinely discover it in production applications.
Certificate pinning — associating the server’s specific public key with the application — provides an additional layer of defence beyond standard chain validation. Even if an attacker obtains a certificate from a trusted CA, pinning will reject it unless the public key matches the pinned value.
What Gets Flagged
// FLAGGED: URLCredential(trust:) unconditionally accepts any certificate
func urlSession(_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, credential) // FLAGGED
}
// FLAGGED: completion handler accepts credential without verifying serverTrust
func urlSession(_ session: URLSession,
task: URLSessionTask,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
}
// FLAGGED: Alamofire server trust evaluation disabled
let manager = Session(serverTrustManager: ServerTrustManager(evaluators: [
"api.example.com": DisabledTrustEvaluator() // FLAGGED
]))
Remediation
Perform proper server trust evaluation in
URLSessionauthentication challenge delegates. Validate the certificate chain against system-trusted roots before callingcompletionHandler(.useCredential, ...).// SAFE: proper server trust evaluation 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 } // Evaluate the server trust using system policy var error: CFError? let isValid = SecTrustEvaluateWithError(serverTrust, &error) if isValid { completionHandler(.useCredential, URLCredential(trust: serverTrust)) } else { completionHandler(.cancelAuthenticationChallenge, nil) } }Use Alamofire’s
PinnedCertificatesTrustEvaluatororPublicKeysTrustEvaluatorfor certificate pinning. This validates both the chain and the specific certificate or key.// SAFE: Alamofire with certificate pinning let evaluators: [String: ServerTrustEvaluating] = [ "api.example.com": PinnedCertificatesTrustEvaluator( certificates: Bundle.main.af.certificates, acceptSelfSignedCertificates: false, performDefaultValidation: true, validateHost: true ) ] let session = Session(serverTrustManager: ServerTrustManager(evaluators: evaluators))Remove test-only validation bypasses before merging to main. Use build flags or separate test configurations so that certificate validation bypasses used for local development are never included in production builds.
// SAFE: bypass only in debug builds #if DEBUG let evaluators: [String: ServerTrustEvaluating] = [ "localhost": DisabledTrustEvaluator() ] #else let evaluators: [String: ServerTrustEvaluating] = [ "api.example.com": PinnedCertificatesTrustEvaluator() ] #endifEnable App Transport Security (ATS). Ensure
NSAllowsArbitraryLoadsis not set totrueinInfo.plist. ATS enforces minimum TLS requirements at the OS level as an additional safety net.
References
- CWE-295: Improper Certificate Validation
- OWASP Mobile Security Testing Guide – MSTG-NETWORK-3 (Certificate Validation)
- Apple – Preventing Insecure Network Connections (App Transport Security)
- Apple – SecTrustEvaluateWithError
- Alamofire – Security Documentation
- CAPEC-94: Adversary in the Middle (CAPEC-94)
- MITRE ATT&CK T1557 – Adversary-in-the-Middle