Weak Self-Signed Certificate and HTTPS Error for IP Address on iOS


Title: SSL Evaluation Fails on iPhone for React Native Expo App

Body:

I am developing a React Native Expo app that needs to configure a device with local WiFi without internet connection. The app works fine on Android by original fetch after implementing the following network_security_config.xml:

<?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>

However, I am facing issues with iOS. I have tried using the react-native-ssl-pinning library and rn-fetch-blob both with and without my cert.pem file, but the TLS check always fails.

Additionally, I attempted to write Swift code to bypass the SSL check and use the certificate, but it also fails.

Here is the error message I received using openssl:

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

The certificate is 512 bits, which seems to be weak and self-signed. I found that apple needs 2048bit certificate.

Questions:

  1. Is the weak certificate the primary reason for the TLS failure on iOS?
  2. How can I use this certificate or bypass this issue temporarily while developing?
  3. Are there any best practices for handling such situations in React Native Expo for iOS?

Thank you in advance for any suggestions or guidance!



iOS Network Configuration Issue: Certificate Invalid Error [-1202]

The application requires making network requests to a local server (https://192.168.x.x/…). I have configured my Info.plist to allow arbitrary loads and have tried adding an exception domain for the local IP. Here is the current configuration for every address:

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

Swift Code

This is my last attempt to ignore a weak certificate for development purposes. Later, we can probably upgrade the certificates on the configured devices, but for now, I need to establish a connection to the IP address and ignore the weak certificate.

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)
        }
    }
}

Error Encountered

All of these configurations result in the following error:

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.}

Request for Help

How can I resolve this certificate issue and ensure my app can communicate with the local server? Is there a better approach to handle SSL pinning or trust evaluation? React Native solution could be best so I dont need manage native code at all.

Any help or guidance would be greatly appreciated.

0

Solution:

I noticed that my urlSession method was never called when it was in my own function file, so I started to investigate where the issue was occurring.

To resolve this, I added a custom urlSession(_:didReceive:completionHandler:) function to the URLSessionSessionDelegateProxy class in the ExpoRequestInterceptorProtocol.swift file. This ensures that the app can properly handle server trust challenges.

Here’s the code I added:

// 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)
    }
}

Location:

I added this function after the following code block in 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

Outcome:

With this change, the app now correctly handles server trust challenges, allowing it to connect to servers with self-signed certificates without issues. It also restores the functionality of the original React Native fetch method.

Question:

Does anyone know how I could handle certificates without modifying the Expo code? Or is this even possible? My next task is to add a certificate and specify the IP address where this function should take effect.

New contributor

Asku is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

1

Issue:

When implementing a custom URLSessionDelegate in Expo module, delegate was being overridden by Expo’s default behavior. This resulted in your custom delegate not being utilized during network requests.

Solution:

The issue arises because Expo may register its own custom URLProtocol classes globally, which can interfere with your custom URLSessionDelegate. To prevent this, you can explicitly clear any globally registered custom URLProtocol classes in your URLSessionConfiguration.

Updated Code:

Here’s the relevant code snippet with the solution applied:

import ExpoModulesCore

public class FetchData_module: Module {
  
  public func definition() -> ModuleDefinition {
    Name("FetchData_module")

    Constants([
      "PI": Double.pi
    ])

    Events("onChange")

    AsyncFunction("fetchData") { (url: String) -> String in
        return try await fetchData(from: url)
    }
  }

  private class CustomURLSessionDelegate: NSObject, URLSessionDelegate {
      func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
          print("CustomURLSessionDelegate: Received authentication challenge")
          
          if let serverTrust = challenge.protectionSpace.serverTrust {
              let credential = URLCredential(trust: serverTrust)
              print("CustomURLSessionDelegate: Accepting server trust")
              completionHandler(.useCredential, credential)
          } else {
              print("CustomURLSessionDelegate: Performing default handling")
              completionHandler(.performDefaultHandling, nil)
          }
      }
  }

    private func fetchData(from urlString: String) async throws -> String {
        guard let url = URL(string: urlString) else {
            throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])
        }
        
        print("Fetching data from URL: (url)")

        let configuration = URLSessionConfiguration.default
        
        configuration.protocolClasses = [] 

        configuration.timeoutIntervalForRequest = 5.0
        configuration.timeoutIntervalForResource = 10.0

        let sessionDelegate = CustomURLSessionDelegate()
        let session = URLSession(configuration: configuration, delegate: sessionDelegate, delegateQueue: nil)

        let (data, _) = try await session.data(from: url)

        if let str = String(data: data, encoding: .utf8) {
            return str
        } else {
            throw NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "Unable to decode data"])
        }
    }
}

Explanation:

  • Issue: Expo may register custom URLProtocol classes that override your custom URLSessionDelegate. This results in your delegate not being called as expected.
  • Solution: To avoid this, set configuration.protocolClasses = [] in your URLSessionConfiguration. This clears any globally registered custom URLProtocol classes, ensuring that your delegate is used.

By applying this change, you can ensure that your custom URLSessionDelegate handles network requests as intended, without interference from Expo’s default behavior.


Important Security Note:

The CustomURLSessionDelegate class in this implementation is configured to accept all HTTPS connections, even those with self-signed or invalid certificates. This means that the delegate will trust any server, potentially exposing your application to man-in-the-middle (MITM) attacks.

If you use this code in production, make sure to implement proper security checks, such as validating the server’s certificate against a known certificate authority or pinning the server’s certificate. Accepting all server certificates without validation should be limited to development environments or controlled scenarios where you are fully aware of the security implications.

Including this note will help others understand the security risks involved and encourage them to take appropriate precautions if they intend to use the code in a production environment.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật