apollo-ios icon indicating copy to clipboard operation
apollo-ios copied to clipboard

Not possible to use WebSocketTransport exclusively on TLS 1.3

Open fortesdev opened this issue 11 months ago • 5 comments

Summary

The following problem was probably always in the iOS Apollo library, and continues in the latest version (1.15.3):

It is not possible to use WebSocketTransport on TLS 1.3 (cipher TLS_AES_256_GCM_SHA384). Our implementation works just fine on endpoints that support TLS 1.2 and other ciphers.

Version

1.15.3 and older

Steps to reproduce the behavior

  1. Target an endpoint that enforces as minimum TLS version 1.3 (cipher suite TLS_AES_256_GCM_SHA384), and try a GraphQL subscription on it. Result: TLSv1.3 SSLHandshake failed (-9836) on real device. OSS error -9836 is errSSLPeerProtocolVersion.

  2. In the web socket client, add enabledSSLCipherSuites = [TLS_AES_256_GCM_SHA384]. Result: FoundationStream's connect method ends with SSLSetEnabledCiphers returning -50. SSLSetEnabledCiphers is deprecated, by the way.

Some notes:

  • Exact same implementation/subscription on similar endpoint that allows TLS 1.2 (with more ciphers) do work on iOS.
  • Our Android and Web implementation with Apollo do work on that endpoint TLS 1.3.
  • Nothing helped to make it work on iOS (disableSSLCertValidation, NSAppTransportSecurity changes, etc.)

Logs

No response

Anything else?

Not sure if the certificate strength can be a factor - The new Digicert G5 root certificate is a 4096 bit one.

fortesdev avatar Jan 08 '25 16:01 fortesdev

Hi @fortesdev, I'm a little stumped on this one. I wonder if it's related to us still targeting iOS 12 and therefore using the old deprecated security APIs? Is there some incompatibility there?

For context - the 2.0 work will allow us to jump up to iOS 15 minimum at which point we can finally move past the deprecated security APIs.

calvincestari avatar Jan 08 '25 18:01 calvincestari

Hi @calvincestari, It is related to the framework used for sockets. As seen here:

"TLS clients using the SecureTransport APIs can’t use TLS 1.3" https://support.apple.com/en-gb/guide/security/sec100a75d12/web

A (potential) solution could be updating the transport implementation in v1.X to match current Starscream, which uses Network.framework while still being iOS 12 compatible. Current Apollo seems inspired by a very old Starscream (2018?).

https://github.com/daltoniam/Starscream

Is it feasible? We'll take a look at this refactor/replacement in the transport layer (without much hope) in our end.

fortesdev avatar Jan 09 '25 08:01 fortesdev

A (potential) solution could be updating the transport implementation in v1.X to match current Starscream, which uses Network.framework while still being iOS 12 compatible.

I do believe we looked into this a long while ago and it was not possible/feasible. I'll have to go digging to see if I can find reference about why that was though.

Current Apollo seems inspired by a very old Starscream (2018?).

That's correct. Our vendored implementation (as of release 1.16.0) is based on the last version of StarScream 3; 3.1.2.

calvincestari avatar Jan 09 '25 18:01 calvincestari

Hi @calvincestari,

I managed to make it work. Can't prepare a PR given it is too tailored to our needs and is kinda dirty still, but pretty much it is as follows:

  1. I removed Apollo/WebSocket from the project's remote dependencies.
  2. Added Apollo/WebSocket as local dependency (to rename a few things causing collisions). This probably can be avoided but I went with a quicker solution, may improve later.
  3. Added Starscream as remote dependency.
  4. Given I could avoided the few collisions, I created the following wrapper class (code for it appended in case it helps someone) OurNewWebSocket.txt ): public class OurNewWebSocket: WebSocket (Startscream's), WebSocketClient(Apollo's), WebSocketDelegate(Apollo's)
  5. The class above connects Starscream's events with the Apollo websocket delegate (the most important/basic events, that's it).
  6. Then I created an ApolloClient using:

/* Starscream socket part / let pinner = FoundationSecurity(allowSelfSigned: true) // don't validate SSL certificates let socket = OurNewWebSocket(request: urlRequest, certPinner: pinner) socket.delegate = socket // itself will transfer Starscrean's events to Apollo's delegate socket.connect() / End of starscream socket part */ ApolloClient(networkTransport: WebSocketTransport(websocket: socket), store: ApolloStore.init())

We still need to test the stability/reconnection and improve the error handling, but it works with TLS 1.3.

fortesdev avatar Jan 09 '25 18:01 fortesdev

We're going to be re-writing the entire web socket layer for 2.0 in the coming months. We've spent a lot of time chasing down race conditions in the existing web socket layer in the past few months, but in order to completely remove these race conditions and dead locks, a rewrite is really necessary.

If you'd like to take a crack at a PR to address this, we're happy to take a look, but we likely won't be investing much more time into tracking these down and instead will focus our efforts on the rewrite.

AnthonyMDev avatar May 09 '25 17:05 AnthonyMDev

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo iOS usage and allow us to serve you better.

github-actions[bot] avatar Nov 13 '25 19:11 github-actions[bot]