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 才会走到。

相关推荐
XKuVhniPguQm19 天前
基于跟踪微分器的智能车辆加速度闭环控制 采用跟踪微分器对加速度进行辨识,并对加速度进行闭环控制
webkit
虹少侠1 个月前
基于 WebKit 构建 macOS 多浮窗视频播放的技术实践(含完整产品落地)
前端·macos·swift·webkit
web小白成长日记2 个月前
前端三个月速成,是否靠谱?
前端·react.js·前端框架·html·reactjs·webkit·scss
blackorbird2 个月前
苹果修复了两个在定向攻击中被利用的Webkit漏洞,其中一个与谷歌ANGLE漏洞同源
前端·webkit
chaoguo12345 个月前
WebKit Insie: WebKit 调试(二)
webkit·调试
2501_915106326 个月前
移动端网页调试实战,iOS WebKit Debug Proxy 的应用与替代方案
android·前端·ios·小程序·uni-app·iphone·webkit
今禾7 个月前
TailwindCSS 与 -webkit-line-clamp 深度解析:现代前端开发的样式革命
前端·css·webkit
chaoguo12341 年前
WebKit Inside: 渲染树
webkit·渲染树
chaoguo12341 年前
WebKit Inside: px 与 pt
css·webkit·px
chaoguo12341 年前
WebKit Inside: CSS 的匹配原理
css·webkit·匹配