You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

URLSession.didReceiveChallenge:如何忽略服务端认证中特定证书校验

macOS下NSURLSession处理IP地址HTTPS请求的TLS证书校验问题

问题描述

我在macOS上用原生NSURLSession发送HTTPS请求,已经实现了didReceiveChallenge回调处理服务端TLS认证。现在有个特殊场景:请求URL用的是IP地址而非域名,想移除URL与服务器证书通用名匹配的默认校验,但保留其他所有校验规则。同时想确认我当前代码是否正确,另外也想问能不能只针对特定连接取消这个校验?

更新:感谢Larme的评论,我调整了逻辑,把主机名检查绑定到特定域名而非IP,代码如下:

- (void)URLSession:(NSURLSession*)session
    didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge
      completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
                                  NSURLCredential* credential))completionHandler {

  if (challenge.protectionSpace.authenticationMethod ==
             NSURLAuthenticationMethodServerTrust) {

    BOOL allow = true;
    if (URL_IS_IP) { 
        SecTrustRef     trust;

        trust = challenge.protectionSpace.serverTrust;
        OSStatus        err;
        SecPolicyRef    policy;

        policy = SecPolicyCreateSSL(true, CFSTR("matchinUrl.com"));
        err = SecTrustSetPolicies(trust, policy);
        if (err == errSecSuccess) {
           allow = evaluateTrust(trust);
        }
        CFRelease(policy);
    } 

    if (allow) {
      completionHandler(
        NSURLSessionAuthChallengeUseCredential, 
        [NSURLCredential credentialForTrust:trust]);
    } else {
      completionHandler(
        NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
  } else {
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  }
}

代码正确性分析

  1. 核心逻辑合理:当请求URL是IP时,通过SecPolicyCreateSSL指定匹配域名matchinUrl.com,替换默认的IP校验策略,这样会用指定域名校验证书的SAN/CN,而非原IP,符合你“跳过IP与证书通用名匹配,保留其他校验”的需求(证书链、有效期等校验由evaluateTrust处理)。
  2. 待修正问题:
    • trust变量仅在URL_IS_IP分支内赋值,若分支不触发,后续使用[NSURLCredential credentialForTrust:trust]会出现野指针错误,需把trust的赋值移到分支外。
    • URL_IS_IP的判断逻辑要确保准确,避免误触发特殊校验。
    • evaluateTrust需完整实现证书校验逻辑(比如证书链有效性、有效期、吊销状态等),不能做简单判断。

修正后的代码

- (void)URLSession:(NSURLSession*)session
    didReceiveChallenge:(NSURLAuthenticationChallenge*)challenge
      completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,
                                  NSURLCredential* credential))completionHandler {

  if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
    BOOL allow = true;
    SecTrustRef trust = challenge.protectionSpace.serverTrust;
    
    if (URL_IS_IP) { 
        OSStatus err;
        SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("matchinUrl.com"));
        err = SecTrustSetPolicies(trust, policy);
        if (err == errSecSuccess) {
           allow = evaluateTrust(trust);
        } else {
            allow = false; // 策略设置失败时拒绝认证
        }
        CFRelease(policy);
    } else {
        // 非IP请求用系统默认校验逻辑
        SecTrustResultType result;
        OSStatus err = SecTrustEvaluate(trust, &result);
        allow = (err == errSecSuccess && (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed));
    }

    if (allow) {
      completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:trust]);
    } else {
      completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
  } else {
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
  }
}

针对特定连接取消校验的实现

可以通过两种方式实现仅针对特定连接跳过IP-证书通用名匹配:

  • 方案1:给任务打标记:创建NSURLSessionDataTask时,用task.taskDescription或自定义关联对象给需要特殊处理的任务打标记,在didReceiveChallenge中通过challenge.task判断标记,再触发特殊校验。
    // 创建任务时添加标记
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    task.taskDescription = @"special_ip_cert_check";
    [task resume];
    
    // 在回调中判断
    if ([challenge.task.taskDescription isEqualToString:@"special_ip_cert_check"]) {
        // 执行自定义校验逻辑
    }
    
  • 方案2:IP列表过滤:提前维护需要特殊处理的IP列表,在回调中判断challenge.protectionSpace.host是否在列表内,仅对列表内IP应用自定义策略。

内容的提问来源于stack exchange,提问作者Zohar81

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 11:16:32