Skip to content

[HIGH] Enforce TLS peer and hostname verification - #62

Open
OskarEichler wants to merge 1 commit into
ostinelli:masterfrom
OskarEichler:codex/security-verify-tls-peers
Open

[HIGH] Enforce TLS peer and hostname verification#62
OskarEichler wants to merge 1 commit into
ostinelli:masterfrom
OskarEichler:codex/security-verify-tls-peers

Conversation

@OskarEichler

Copy link
Copy Markdown

Summary

Enable OpenSSL's secure client defaults for every HTTPS connection and verify the TLS origin hostname, including connections tunneled through an HTTP proxy. This prevents untrusted endpoints from impersonating the requested HTTPS server.

Security impact and urgency

HIGH. NetHttp2::Client currently constructs an OpenSSL::SSL::SSLContext without calling set_params. Ruby's default context has verify_mode == VERIFY_NONE and hostname verification disabled, so HTTPS connections accept self-signed or otherwise untrusted certificates. A network-positioned attacker, poisoned DNS response, or malicious proxy can terminate TLS, read request headers and bodies, capture bearer credentials, and forge responses with the application's network privileges.

This affects both the default context and caller-provided contexts that do not explicitly enable verification. Apnotic uses both paths for Apple Push Notification connections, including bearer-token authentication.

Reproduction

A focused local TLS server presents a fresh self-signed certificate that is absent from the client's trust store. Current 0.19.0/current master accepts it and reports:

untrusted=[:accepted, 0, false]

The final values are verify_mode and verify_hostname. With this patch, the same peer is rejected with certificate verify failed (self-signed certificate). Adding that certificate to a custom X509::Store succeeds and reports:

trusted=[:accepted, 1, true]

Implementation

  • Call SSLContext#set_params for HTTPS contexts, enabling certificate-chain and hostname verification plus the platform trust store.
  • Set SSLSocket#hostname to the requested origin. For a CONNECT proxy, TLS terminates at the origin through the tunnel, so using the proxy address for SNI/verification is incorrect. This also resolves the hostname problem reported in Proxy not working #30.
  • Preserve custom client certificates, private keys, and custom certificate stores on caller-provided contexts.

Verification

  • Self-signed rejection and explicitly trusted private-CA acceptance pass on Ruby 3.2.11 and Ruby 4.0.6.
  • Apnotic 1.8.0 token-auth integration confirms VERIFY_PEER, hostname verification, and the Apple APNs origin on Ruby 4.0.6.
  • A bounded live handshake with api.push.apple.com:443 succeeds with TLS 1.3 and verify_result == 0; no APNs request or production operation was performed.
  • All eight runtime Ruby files pass syntax checks on Ruby 3.2.11 and Ruby 4.0.6.
  • gem build net-http2.gemspec succeeds for 0.19.0.
  • With an external shim that trusts the repository's self-signed test certificate, the unchanged existing suite has 70 passing examples on both Ruby versions. Its only failure is the stale proxy assertion that explicitly expects the proxy URL as the TLS hostname; the corrected origin hostname is the behavior under review. Repository tests were not changed.
  • git diff --check passes.

Limitations

The local model covers trust-chain and hostname-verification configuration, not every TLS version, cipher, operating-system trust store, proxy implementation, or network failure mode. The live check validates only a TLS handshake. Existing ALPN, proxy I/O, timeout, and stream-lifecycle issues are outside this focused fix.

Breaking-change note

HTTPS endpoints using self-signed or privately issued certificates will now fail unless their CA is explicitly added to a custom context's cert_store. Callers that deliberately relied on unauthenticated TLS must configure trust instead. Proxied TLS now sends and verifies the origin hostname rather than the proxy address.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant