iOS 上的自签名证书较弱以及 IP 地址的 HTTPS 错误

问题描述 投票:0回答:1

标题:React Native Expo 应用程序在 iPhone 上的 SSL 评估失败

身体:

我正在开发一个 React Native Expo 应用程序,需要在没有互联网连接的情况下使用本地 WiFi 配置设备。在实现以下

network_security_config.xml
后,该应用程序通过原始 fetch 在 Android 上运行良好:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:tools="http://schemas.android.com/tools">
    <base-config>
        <trust-anchors>
            <certificates src="user" />
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    <domain-config>
        <domain includeSubdomains="true">192.168.x.x</domain>
        <trust-anchors>
            <certificates src="@raw/mycert"
                tools:ignore="NetworkSecurityConfig" />
        </trust-anchors>
    </domain-config>
</network-security-config>

但是,我在使用 iOS 时遇到了问题。我尝试过使用

react-native-ssl-pinning
库和
rn-fetch-blob
(带或不带
cert.pem
文件),但 TLS 检查总是失败。

此外,我尝试编写Swift代码来绕过SSL检查并使用证书,但也失败了。

这是我使用

openssl
收到的错误消息:

SSL handshake has read 601 bytes and written 465 bytes
Verification error: EE certificate key too weak

证书是512位,看起来很弱并且是自签名的。我发现苹果需要2048位证书。

问题:

  1. 弱证书是 iOS 上 TLS 失败的主要原因吗?
  2. 如何在开发时使用此证书或暂时绕过此问题?
  3. React Native Expo for iOS 中是否有处理此类情况的最佳实践?

预先感谢您的任何建议或指导!



iOS 网络配置问题:证书无效错误 [-1202]

应用程序需要向本地服务器发出网络请求(https://192.168.x.x/...)。我已将 Info.plist 配置为允许任意加载,并尝试为本地 IP 添加例外域。这是每个地址的当前配置:

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>

快速代码

这是我出于开发目的而忽略弱证书的最后一次尝试。稍后,我们可能可以升级已配置设备上的证书,但现在,我需要建立到 IP 地址的连接并忽略弱证书。

import Foundation

@objc(RNSecureRequest)
class RNSecureRequest: NSObject {

    @objc(performDebugRequest:reject:)
    func performDebugRequest(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
        guard let url = URL(string: "https://192.168.x.x/init/...data") else {
            reject("Invalid URL", "The URL provided is invalid", nil)
            return
        }

        let sessionConfig = URLSessionConfiguration.default
        let session = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)

        let task = session.dataTask(with: url) { data, response, error in
            if let error = error {
                reject("Request Error", error.localizedDescription, error)
                return
            }

            guard let data = data else {
                reject("No Data", "No data received from the server", nil)
                return
            }

            let responseString = String(data: data, encoding: .utf8)
            resolve(responseString)
        }

        task.resume()
    }
}

extension RNSecureRequest: URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if let serverTrust = challenge.protectionSpace.serverTrust {
            // Create a policy to allow the connection to proceed despite the weak certificate.
            let policy = SecPolicyCreateSSL(true, challenge.protectionSpace.host as CFString)
            SecTrustSetPolicies(serverTrust, policy)

            // Evaluate the trust object using the modern API.
            var error: CFError?
            let isServerTrusted = SecTrustEvaluateWithError(serverTrust, &error)

            if isServerTrusted {
                let credential = URLCredential(trust: serverTrust)
                completionHandler(.useCredential, credential)
            } else {
                // Even if the evaluation fails, you can bypass it if needed.
                let credential = URLCredential(trust: serverTrust)
                completionHandler(.useCredential, credential)
            }
        } else {
            completionHandler(.performDefaultHandling, nil)
        }
    }
}

遇到错误

所有这些配置都会导致以下错误:

Connection 15: default TLS Trust evaluation failed(-9807)
Connection 15: TLS Trust encountered error 3:-9807
Connection 15: encountered error(3:-9807)
Task <F30FCF7F-8C61-42D7-8F64-DF19C7D426DF>.<4> HTTP load failed, 0/0 bytes (error code: -1202 [3:-9807])
Task <F30FCF7F-8C61-42D7-8F64-DF19C7D426DF>.<4> finished with error [-1202] Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.x.x” which could put your confidential information at risk." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x14703a800) s: DeviceDrive i: DeviceDrive>"
), NSErrorClientCertificateStateKey=0, NSErrorFailingURLKey=https://192.168.x.x/init?ssid=&pwd=&token=url=data, NSErrorFailingURLStringKey=https://192.168.x.x/init?ssid=&pwd=&token=url=data, NSUnderlyingError=0x30118c5d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x302ec0dc0>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9807, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9807, kCFStreamPropertySSLPeerCertificates=(
    "<cert(0x14703a800) s: DeviceDrive i: DeviceDrive>"
)}}, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <F30FCF7F-8C61-42D7-8F64-DF19C7D426DF>.<4>"
), _kCFStreamErrorCodeKey=-9807, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <F30FCF7F-8C61-42D7-8F64-DF19C7D426DF>.<4>, NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x302ec0dc0>, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “192.168.x.x” which could put your confidential information at risk.}

请求帮助

如何解决此证书问题并确保我的应用程序可以与本地服务器通信?是否有更好的方法来处理 SSL 固定或信任评估? React Native 解决方案可能是最好的,所以我根本不需要管理本机代码。

任何帮助或指导将不胜感激。

swift react-native ssl-certificate fetch self-signed-certificate
1个回答
0
投票

解决方案:

我注意到我的 urlSession 方法在我自己的函数文件中时从未被调用,因此我开始调查问题发生的位置。

为了解决此问题,我向

urlSession(_:didReceive:completionHandler:)
文件中的
URLSessionSessionDelegateProxy
类添加了自定义
ExpoRequestInterceptorProtocol.swift
函数。这确保应用程序可以正确处理服务器信任挑战。

这是我添加的代码:

// MARK: - Custom URL Session Delegate Function
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
    if let serverTrust = challenge.protectionSpace.serverTrust {
        let credential = URLCredential(trust: serverTrust)
        print("Accepting server trust in URLSessionSessionDelegateProxy")
        completionHandler(.useCredential, credential)
    } else {
        completionHandler(.performDefaultHandling, nil)
    }
}

地点:

我在

node_modules/expo-modules-core/ios/DevTools/ExpoRequestInterceptorProtocol.swift
中的以下代码块之后添加了此功能:

/private class URLSessionSessionDelegateProxy: NSObject, URLSessionDataDelegate {
    private var requestIdProvider = RequestIdProvider()
    private var delegateMap: [String: URLSessionDataDelegate] = [:]
    private let dispatchQueue = ExpoRequestCdpInterceptor.shared.dispatchQueue

结果:

通过此更改,该应用程序现在可以正确处理服务器信任挑战,使其能够毫无问题地连接到具有自签名证书的服务器。它还恢复了原始 React Native fetch 方法的功能。

问题:

有谁知道如何在不修改Expo代码的情况下处理证书?或者这有可能吗?我的下一个任务是添加证书并指定此功能应生效的 IP 地址。

© www.soinside.com 2019 - 2024. All rights reserved.