webview的SSL异常是webview加载失败的重要原因之一,因此有必要对此进行较为全面的监控。我们的业务中关于webview的SSL异常监控起初是这样做的:
监控的位置:
时机一:数据加载过程中出错
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
时机二:数据渲染时出错
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
这里,NSError对象会返回任意webkit层面或者CFNetWork或者iOS系统内核层面的错误,我们据此在原生层面进行监控。
我们的监控是在业务的问题中逐步完善起来的。
第一阶段:NSURLErrorDomain层面的SSL异常,在NSURLError.h文件中列举的SSL异常包括以下几种:
// SSL errors
NSURLErrorSecureConnectionFailed = -1200,
NSURLErrorServerCertificateHasBadDate = -1201,
NSURLErrorServerCertificateUntrusted = -1202,
NSURLErrorServerCertificateHasUnknownRoot = -1203,
NSURLErrorServerCertificateNotYetValid = -1204,
NSURLErrorClientCertificateRejected = -1205,
NSURLErrorClientCertificateRequired = -1206,
NSURLErrorCannotLoadFromNetwork = -2000,
我们再上报当前错误类型的同时会去反解析出当前错误域名的服务器ip地址,进行统一上报,方便运维侧定位问题。
第二阶段:奇怪的unknown code
在一次大促中,有用户反馈,m页加载为白页,通过检查监控,我们发现SSL没有异常,但在“时机一”上报了一些奇怪的异常:
NSCocoaErrorDomain-Error Domain=NSPOSIXErrorDomain Code=-9805 "Unknown error: -9805" UserInfo={_WKRecoveryAttempterErrorKey=, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <49D0CCA2-77C3-41B9-9C41-49E96A033624>.<639>, _kCFStreamErrorDomainKey=1, _kCFStreamErrorCodeKey=-9805}
这里异常在我们整体异常中,返回的code为-9805,百度了一圈竟然没有发现什么有价值的东西。后来在Stack Overflow中一个近似的回答中找到答案:
https://opensource.apple.com/source/libsecurity_ssl/libsecurity_ssl-32463/lib/SecureTransport.h
原来这是苹果在自己的Security Framework中定义的一种NSPOSIXErrorDomain 的SSL异常:
CF_ENUM(OSStatus) {
errSSLProtocol = -9800, /* SSL protocol error */
errSSLNegotiation = -9801, /* Cipher Suite negotiation failure */
errSSLFatalAlert = -9802, /* Fatal alert */
errSSLWouldBlock = -9803, /* I/O would block (not fatal) */
errSSLSessionNotFound = -9804, /* attempt to restore an unknown session */
errSSLClosedGraceful = -9805, /* connection closed gracefully */
errSSLClosedAbort = -9806, /* connection closed via error */
errSSLXCertChainInvalid = -9807, /* invalid certificate chain */
errSSLBadCert = -9808, /* bad certificate format */
errSSLCrypto = -9809, /* underlying cryptographic error */
errSSLInternal = -9810, /* Internal error */
errSSLModuleAttach = -9811, /* module attach failure */
errSSLUnknownRootCert = -9812, /* valid cert chain, untrusted root */
errSSLNoRootCert = -9813, /* cert chain not verified by root */
errSSLCertExpired = -9814, /* chain had an expired cert */
errSSLCertNotYetValid = -9815, /* chain had a cert not yet valid */
errSSLClosedNoNotify = -9816, /* server closed session with no notification */
errSSLBufferOverflow = -9817, /* insufficient buffer provided */
errSSLBadCipherSuite = -9818, /* bad SSLCipherSuite */
/* fatal errors detected by peer */
errSSLPeerUnexpectedMsg = -9819, /* unexpected message received */
errSSLPeerBadRecordMac = -9820, /* bad MAC */
errSSLPeerDecryptionFail = -9821, /* decryption failed */
errSSLPeerRecordOverflow = -9822, /* record overflow */
errSSLPeerDecompressFail = -9823, /* decompression failure */
errSSLPeerHandshakeFail = -9824, /* handshake failure */
errSSLPeerBadCert = -9825, /* misc. bad certificate */
errSSLPeerUnsupportedCert = -9826, /* bad unsupported cert format */
errSSLPeerCertRevoked = -9827, /* certificate revoked */
errSSLPeerCertExpired = -9828, /* certificate expired */
errSSLPeerCertUnknown = -9829, /* unknown certificate */
errSSLIllegalParam = -9830, /* illegal parameter */
errSSLPeerUnknownCA = -9831, /* unknown Cert Authority */
errSSLPeerAccessDenied = -9832, /* access denied */
errSSLPeerDecodeError = -9833, /* decoding error */
errSSLPeerDecryptError = -9834, /* decryption error */
errSSLPeerExportRestriction = -9835, /* export restriction */
errSSLPeerProtocolVersion = -9836, /* bad protocol version */
errSSLPeerInsufficientSecurity = -9837, /* insufficient security */
errSSLPeerInternalError = -9838, /* internal error */
errSSLPeerUserCancelled = -9839, /* user canceled */
errSSLPeerNoRenegotiation = -9840, /* no renegotiation allowed */
/* non-fatal result codes */
errSSLPeerAuthCompleted = -9841, /* peer cert is valid, or was ignored if verification disabled */
errSSLClientCertRequested = -9842, /* server has requested a client cert */
/* more errors detected by us */
errSSLHostNameMismatch = -9843, /* peer host name mismatch */
errSSLConnectionRefused = -9844, /* peer dropped connection before responding */
errSSLDecryptionFail = -9845, /* decryption failure */
errSSLBadRecordMac = -9846, /* bad MAC */
errSSLRecordOverflow = -9847, /* record overflow */
errSSLBadConfiguration = -9848, /* configuration error */
errSSLUnexpectedRecord = -9849, /* unexpected (skipped) record in DTLS */
errSSLWeakPeerEphemeralDHKey = -9850, /* weak ephemeral dh key */
/* non-fatal result codes */
errSSLClientHelloReceived = -9851, /* SNI */
/* fatal errors resulting from transport or networking errors */
errSSLTransportReset = -9852, /* transport (socket) shutdown, e.g., TCP RST or FIN. */
errSSLNetworkTimeout = -9853, /* network timeout triggered */
/* fatal errors resulting from software misconfiguration */
errSSLConfigurationFailed = -9854, /* TLS configuration failed */
/* additional errors */
errSSLUnsupportedExtension = -9855, /* unsupported TLS extension */
errSSLUnexpectedMessage = -9856, /* peer rejected unexpected message */
errSSLDecompressFail = -9857, /* decompression failed */
errSSLHandshakeFail = -9858, /* handshake failed */
errSSLDecodeError = -9859, /* decode failed */
errSSLInappropriateFallback = -9860, /* inappropriate fallback */
errSSLMissingExtension = -9861, /* missing extension */
errSSLBadCertificateStatusResponse = -9862, /* bad OCSP response */
errSSLCertificateRequired = -9863, /* certificate required */
errSSLUnknownPSKIdentity = -9864, /* unknown PSK identity */
errSSLUnrecognizedName = -9865, /* unknown or unrecognized name */
};
在这里我们得到了答案,原来-9805是ssl连接被终止了。到这里,问题基本可以定位为Server端的ssl的策略可能存在问题了,反馈给运维去查就行,而针对客户端,后续我们的ssl异常监控将纳入以上异常。