swift 尝试了解URLSession身份验证挑战

gajydyqb  于 2022-12-17  发布在  Swift
关注(0)|答案(2)|浏览(191)

我正在尝试从URL下载PDF。

private func downloadSessionWithFileURL(_ url: URL){
    var request = URLRequest(url: url)
    
    request.addValue("gzip, deflate", forHTTPHeaderField: "Accept-Encoding")
   
    let sessionConfig = URLSessionConfiguration.default
    
    let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
    session.downloadTask(with: request).resume()      
}

这将调用其委托方法

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    if challenge.previousFailureCount > 0 {
          completionHandler(Foundation.URLSession.AuthChallengeDisposition.cancelAuthenticationChallenge, nil)
    }
    if let serverTrust = challenge.protectionSpace.serverTrust {
      completionHandler(Foundation.URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust: serverTrust))
} else {
      print("unknown state. error: \(String(describing: challenge.error))")
   }
}

URLAuthenticationChallenges protectionSpace始终为serverTrust。当尝试访问PDF的URL时,它会将用户重定向到登录屏幕。我还以为会有另一个调用

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)

要求用户输入他们的凭据,但没有。因此下载任务尝试下载重定向URL的内容,这是一个登录屏幕。
我的问题是。
1.什么会触发用户名和密码的URLAuthenticationChallenge。它是HTML中的特定头值吗?
1.对于来自服务器的用户名和密码请求,我应该期待哪个URLAuthenticationChallenge protectionSpace。

4uqofj5v

4uqofj5v1#

有两种不同的委托协议:用于URLSession本身及其任务。
URL会话委托具有:public func urlSession(_:didReceive:completionHandler:) URL会话任务委托具有:public func urlSession(_:task:didReceive:completionHandler:)
URLSessionDelegate用于服务器信任问题(例如,在通过Charles或其他代理运行时允许SSL信任)。URLSessionTaskDelegate用于单个任务的身份验证。
因此,要获得身份验证挑战,请将以下代码添加到类中:

extension MyClass: URLSessionTaskDelegate {

    public func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {

        if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodDefault ||
            challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodHTTPBasic {

            let credential = URLCredential(user: self.basicAuthUserName,
                                           password: self.basicAuthPassword,
                                           persistence: .forSession)

            completionHandler(.useCredential, credential)
        }
        else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}
k75qkfdt

k75qkfdt2#

SSL的一些基本知识:

How SSL works? When client establishes the connection with server (called SSL handshake):
Client connects to server and requests server identify itself.
Server sends certificate to client (include public key)
Client checks if that certificate is valid. If it is, client creates a symmetric key (session key), encrypts with public key, then sends back to server
Server receives encrypted symmetric key, decrypts by its private key, then sends acknowledge packet to client
Client receives ACK and starts the session

1.什么会触发用户名和密码的URLAuthenticationChallenge。它是HTML中的特定头值吗?
如果你有https连接,这些方法将被触发。这些是出于安全目的,以防止中间人攻击。例如,我可以设置查尔斯代理服务器,安装在模拟器/设备的公共证书,并可以监视所有的请求,该应用程序发送到实际的服务器,从而获得敏感信息(API密钥,令牌,请求头,请求体等),我需要隐藏攻击者。
对于来自服务器的用户名和密码请求,我应该期待哪个URLAuthenticationChallenge protectionSpace。
您可以将服务器证书与应用中的本地证书进行比较:

if let serverCertificate = SecTrustGetCertificateAtIndex(trust, 0) {

       let serverCertificateData = SecCertificateCopyData(serverCertificate) as Data
       let localCer = Bundle.main.path(forResource: "fileName", ofType: "cer")

        if let localCer = localCer {

             if localCer.isEqual(to: serverCertificate) {                                             completionHandler(URLSession.AuthChallengeDisposition.useCredential, URLCredential(trust:serverTrust))
                 return
             }
         }
  }

也可以比较公钥:

if let serverCertificate = SecTrustGetCertificateAtIndex(trust, 0), let serverCertificateKey = publicKey(for: serverCertificate) {
        if pinnedKeys().contains(serverCertificateKey) {
            completionHandler(.useCredential, URLCredential(trust: trust))
            return
        }
    }

比较公钥是一种更好的方法,因为在比较证书时,您必须在应用程序中保留本地证书的副本,当证书到期时,您必须更新应用程序中的证书,这需要在应用程序商店中更新。

相关问题