smogon / pokemon-showdown-client

The client for Pokémon Showdown
http://pokemonshowdown.com
GNU Affero General Public License v3.0
560 stars 786 forks source link

Broken TLS when connecting to psim.us with self-hosted server with extra periods in hostname #1995

Closed raymond-h closed 1 year ago

raymond-h commented 2 years ago

Unsure where else to post this, as it is seemingly not an issue with the client as much as it's the web server hosting *.psim.us.

When self-hosting a server, the officially-hosted client is still used with a domain of [server-hostname].psim.us. This works fine, when connecting with plain HTTP to that hostname. If [server-hostname] contains no extra periods, using HTTPS also works fine.

However, if you use HTTPS and [server-hostname] contains any extra periods, the browser will complain about failing to establish a TLS connection, with any variant of "no matching ciphers" as error message. This is a problem since regardless if you are hosting the server on a custom domain or just using its IP address, it's almost guaranteed there will be multiple periods in [server-hostname], making it impossible to use HTTPS for the client itself if playing on a self-hosted server.

Example: https://ab.psim.us/ loads the client just fine (and fails to connect to server at domain ab, but that is irrelevant - what matters is that the client loads), while https://a.b.psim.us/ reports a problem connecting with error SSL_ERROR_NO_CYPHER_OVERLAP in Firefox.

openssl version: OpenSSL 1.1.1m 14 Dec 2021

Output from running echo -n | openssl s_client -connect a.b.psim.us:443 -servername a.b.psim.us:

139729484100928:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:ssl/record/rec_layer_s3.c:1544:SSL alert number 40
CONNECTED(00000003)
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 313 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

Output from echo -n | openssl s_client -connect ab.psim.us:443 -servername ab.psim.us (slightly abbreviated):

depth=2 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
verify return:1
depth=1 C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = sni.cloudflaressl.com
verify return:1
CONNECTED(00000003)
---
Certificate chain
 0 s:C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = sni.cloudflaressl.com
   i:C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
 1 s:C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3
   i:C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFLzCCBNWgAwIBAgIQAZIy24f9vHLosbdQL+OSKDAKBggqhkjOPQQDAjBKMQsw
[...]
UwIgdNuv7rFmNMCVmjY1uAjyjRf6V4jFoq5pvBs0T0zD9qc=
-----END CERTIFICATE-----
subject=C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = sni.cloudflaressl.com

issuer=C = US, O = "Cloudflare, Inc.", CN = Cloudflare Inc ECC CA-3

---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2777 bytes and written 397 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-ECDSA-CHACHA20-POLY1305
    [...]
---
DONE
EverOddish commented 1 year ago

I've had the exact same problem trying to host a server on both DigitalOcean and AWS, each with separate domain names. I would be interested in helping to find a solution.

EverOddish commented 1 year ago

I'm not at all familiar with the code base, but I noticed this URL regex includes a literal '.' character:

https://github.com/smogon/pokemon-showdown-client/blob/master/crossdomain.php#L7

...and this other regex does not:

https://github.com/smogon/pokemon-showdown-client/blob/master/config/config-example.inc.php#L37

...which is used here to verify cross-domain requests:

https://github.com/smogon/pokemon-showdown-client/blob/master/lib/dispatcher.lib.php#L79

Perhaps related to this issue?

monsanto commented 1 year ago

Sorry, wildcard certificates don't support an arbitrary number of levels, and we aren't going to register extra certificates to support this use-case.

raymond-h commented 1 year ago

That is perfectly understandable. As far as I know, because any and all domain names and IP addresses are going to contain at minimum one period, that leaves exactly one single option left - accessing the client using plain HTTP. Unfortunately this is a huge problem for my group of friends, because we've tried that plenty of times for many months now but keep hitting a problem that won't let that happen.

Specifically the problem is that for a handful of us in said group, their browser(s) will entirely at random just refuse to use HTTP for the Showdown client, and always enforce use of HTTPS (and being met with the above TLS issue). We have no idea what causes it, but we've tried pretty much everything there is to try - wiping every imaginable cache, trying different browsers, inspected the network traffic in the browser console... Sometimes it starts working again, but then sometimes it does not, and none of it provides a real lead on what's going on. The only thing close to a clue is that Chrome-based browsers do the redirect by apparently returning a 307 Internal Redirect from cache with the header Non-Authoritative-Reason: DNS, which leads to only a handful of Google results, none of which provide any further help.

It is a common enough issue that every time our friend group meets up (6-7 people), there's always 1-2 people who are hit by it and are completely unable to get around it, no matter what we try. I would've loved to have those issues with my friends' browsers just solved somehow instead, but we're at our wit's end with it.

What if the client was changed so the server address wasn't directly part of the client URL? Like what if periods were replaced with some other character(s)? Or maybe the server address as a whole was encoded with Base64 or something similar? It should be fairly straightforward and non-intrusive change to then just decode that before connecting to the server via Websocket, and TLS would no longer be a problem because the client address would no longer contain extra periods. I'm willing to contribute that change, if you consider that to be a good idea.

DaWoblefet commented 1 year ago

RE: 307 redirects, we were having the same issue a few months ago because of browser updates shipping new settings. In Chrome, I had to disable the "Async DNS resolver" flag. In Firefox, you have to disable "Enable DNS over HTTPS". It's currently documented as a workaround: https://github.com/smogon/pokemon-showdown/blob/master/CONTRIBUTING.md