WebKit Authentication Challenge 核心流程

我们知道代码发起 URLSession 请求时,会有个-URLSession:didReceiveChallenge:completionHandler:代理函数自定义认证逻辑;在使用 WebKit 时,也有个-webView:didReceiveAuthenticationChallenge:completionHandler:代理函数实现同样功能。

不难推测,WebKit 底层是使用 URLSession 时把认证函数转接到外部供开发者使用的。不过在实际的开发过程中却发现了不回调的情况,不得不挖一下内部逻辑。

Network 进程的漏斗

WebKit 是在 NetworkSessionCocoa 里面做网络请求回执处理的,跟着 Authentication Challenge 代理回调逻辑发现:

ini 复制代码
    // step1
    // Proxy authentication is handled by CFNetwork internally. We can get here if the user cancels
    // CFNetwork authentication dialog, and we shouldn't ask the client to display another one in that case.
    if (challenge.protectionSpace.isProxy
        && sessionCocoa->proxyConfigs().isEmpty()
        && !sessionCocoa->preventsSystemHTTPProxyAuthentication())
        return completionHandler(NSURLSessionAuthChallengeUseCredential, nil);

    // step2
    NegotiatedLegacyTLS negotiatedLegacyTLS = NegotiatedLegacyTLS::No;
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        NSURLSessionTaskTransactionMetrics *metrics = task._incompleteTaskMetrics.transactionMetrics.lastObject;
        auto tlsVersion = (tls_protocol_version_t)metrics.negotiatedTLSProtocolVersion.unsignedShortValue;
        if (tlsVersion == tls_protocol_version_TLSv10 || tlsVersion == tls_protocol_version_TLSv11)
            negotiatedLegacyTLS = NegotiatedLegacyTLS::Yes;
        if (negotiatedLegacyTLS == NegotiatedLegacyTLS::No && [task respondsToSelector:@selector(_TLSNegotiatedProtocolVersion)]) {
            SSLProtocol tlsVersion = [task _TLSNegotiatedProtocolVersion];
            if (tlsVersion == kTLSProtocol11 || tlsVersion == kTLSProtocol1)
                negotiatedLegacyTLS = NegotiatedLegacyTLS::Yes;
        }

	// step3
        if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes && task._preconnect)
            return completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    ...

分析相关上下文解读核心含义如下。

step1

若是代理认证且用户主动拒绝了认证弹窗,则默认处理,意味着不会回调到 WebKit 外部代理供开发者拦截。

step2

首先从 URLSession 里面的 metrics 性能数据里面读取 TLS 的版本,然后从 NSURLSessionTask 里面去 TLS 版本。 最终,若 TLS 版本是 1.0 和 1.1 则认为是 legacy TLS,估计是由于历史遗留问题不得不做特殊处理。

这里用到了几个枚举类型,tls_protocol_version_t 的值其实和 swift 里面的 tls_protocol_version_t 枚举值是一样的,kTLSProtocol1 的值也就是 Secure 系统库里面的枚举。

step3

若是 legacy TLS 且支持了 preconnect 则默认处理。preconnect 就是在网络进程还没有去加载网页的资源时,提前去与 host 对应的服务进行预连接,这个能力可能是开启的。

所以有漏斗1:若 TLS 版本低了可能永远无法走到自定义认证函数。

后续还会存在一些是否合并 challenge 的判定,如果是 serverTrust 则会正常流转。

php 复制代码
static bool canCoalesceChallenge(const WebCore::AuthenticationChallenge& challenge) {
    // Do not coalesce server trust evaluation requests because ProtectionSpace comparison does not evaluate server trust (e.g. certificate).
    return challenge.protectionSpace().authenticationScheme() != ProtectionSpace::AuthenticationScheme::ServerTrustEvaluationRequested;
}

所以有漏斗2:若 TLS 认证被网络库错误的判定为非 serverTrust。

网络库判定 serverTrust 逻辑

把 Swift 的 URLSession 源码搞下来看了下,发现竟然不支持 NSURLAuthenticationMethodServerTrust 类型,跟 OC 的实现估计是大不相同,所以这部分还不太好查。当然这也符合预期,就像 Swift URLCache 的实现也非常拙劣,跟 OC 完全不一样。

UI 进程的漏斗

目标逻辑:

scss 复制代码
void WebPageProxy::didReceiveAuthenticationChallengeProxy(...) {
    if (negotiatedLegacyTLS == NegotiatedLegacyTLS::Yes) {
        m_navigationClient->shouldAllowLegacyTLS(...) {
            if (shouldAllowLegacyTLS)
                m_navigationClient->didReceiveAuthenticationChallenge(*this, authenticationChallenge.get());
            else
                authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::Cancel);
        });
        return;
    }
    m_navigationClient->didReceiveAuthenticationChallenge(*this, authenticationChallenge.get());
}

如果是 Legacy TLS 会走 shouldAllowLegacyTLS 函数逻辑:

  • 优先判定-webView:authenticationChallenge:shouldAllowDeprecatedTLS:
  • 其次判定 -_webView:authenticationChallenge:shouldAllowLegacyTLS:
  • 兜底websiteDataStore().configuration().legacyTLSEnabled() (默认 true);

而默认情况下,前两步都不会走到,所以这部分理论上不会有漏斗。而且这也是在网络进程判定是 Legacy TLS 且不支持 preconnect 才会走到。

相关推荐
波儿菜2 小时前
JS requestAnimationFrame 底层实现
javascript·webkit
一丝晨光2 个月前
Chrome和Chromium的区别?浏览器引擎都用的哪些?浏览器引擎的作用?
前端·c++·chrome·webkit·chromium·blink·trident
jyl_sh2 个月前
WebKit(适用2024年11月份版本)
前端·浏览器·客户端·webkit
狂小虎5 个月前
亲测解决Bundler HTTPError Could not fetch specs from
https·wifi·html·ruby·webkit·gemfile
hong1616885 个月前
探索WebKit的奥秘:打造高效、兼容的现代网页应用
前端·webkit
liuxin334455665 个月前
Webkit与Web Push API:提升用户体验的推送技术
webkit
Thanks_ks5 个月前
WebKit 的简介及工作流程
跨平台·webkit·高性能·事件处理·浏览器引擎·dom 操作·网页渲染
小刘哥0075 个月前
探索 WebKit 的奥秘:构建高效、兼容的现代网页应用
webkit
NiNg_1_2345 个月前
Webkit简介及工作流程
前端·webkit