OKHttp: authenticated TLS by default, credentials withheld from unauthenticated servers - #2126
Open
abhinav-phi wants to merge 2 commits into
Open
OKHttp: authenticated TLS by default, credentials withheld from unauthenticated servers#2126abhinav-phi wants to merge 2 commits into
abhinav-phi wants to merge 2 commits into
Conversation
…henticated servers (apache#2079) http.trust.everything shipped enabled with a static SSLContext('SSL'), a no-op trust manager and a hostname verifier that accepted any name: anybody able to answer for a host name received the Authorization header built from http.basicauth.*, credential headers from http.custom.headers and the replayed cookies. - http.trust.everything now defaults to false: certificate chains are validated and the servers are authenticated, as a normal TLS client would do. Self-signed intranet hosts need the operator to opt in - the trust-all context is built from 'TLS' instead of 'SSL' - hostname verification is a separate decision (http.verify.hostnames, default true) and is no longer disabled as a side effect of trusting any certificate - credentials are withheld over connections whose server certificate was not validated, unless http.credentials.allow.insecure is set: basic auth, credential headers (authorization, proxy-authorization, cookie, x-api-key) in http.custom.headers, headers set by request and cookies are all covered - every insecure state logs a WARN naming the keys, and the keys are surfaced in crawler-default.yaml and the archetype crawler-conf.yaml files
The JDK's com.sun.net.httpserver classes are rejected by the forbiddenapis check (jdk-non-portable). WireMock is already a test dependency and serves HTTPS from a PKCS12 keystore; the request journal replaces the hand-written header recorder. The custom headers use the Name=value syntax that http.custom.headers parses and the code is google-java-format clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2079
The problem
HttpProtocol.configure()readhttp.trust.everythingwith a default of true and, when set (which was the default), installed two things at once:X509TrustManagerbuilt on a staticSSLContext.getInstance("SSL"), accepting any certificate chain — self-signed, expired, revoked, or issued for a completely different name; andHostnameVerifierthat returned true for every host name.Because these two travelled together, a single undocumented key silently disabled both halves of server authentication, with three consequences:
Authorizationheader built fromhttp.basicauth.user/http.basicauth.password, credential headers configured throughhttp.custom.headers, headers set per request via metadata, and cookies replayed fromhttp.use.cookieswere all sent on connections where nobody had verified who was on the other end. Anybody able to answer for the host name — a DNS hijack, a hostile network on path, an intercepted robots.txt redirect — received them. This was verified in the issue: a crawl with basic auth pointed at an HTTPS server presenting a self-signed certificate showed theAuthorizationheader arriving."SSL"rather than"TLS", a relic that limits the enabled protocol families.And as the issue notes, the key was set in no shipped YAML and nothing was logged: operators inherited all of this rather than choosing it.
The fix
http.trust.everythingnow defaults tofalse. Certificate chains are validated and servers are authenticated, as a normal TLS client would do. Crawling hosts with unvalidatable certificates (e.g. self-signed intranet ones) remains possible — through an explicit, documented opt-in.TLSinstead ofSSL.http.verify.hostnameskey (defaulttrue) controls it independently: trusting any certificate no longer implies accepting any name. Disabling it is a separate, separately-logged decision.authorization,proxy-authorization,cookie,x-api-key— case-insensitive) fromhttp.custom.headers, credential headers set per request, and cookies, unless the operator explicitly setshttp.credentials.allow.insecure: true. Ordinary headers (user agent, accept, custom non-credential ones, …) are unaffected.http.trust.everything,http.verify.hostnamesandhttp.credentials.allow.insecureare documented incrawler-default.yaml, the archetypecrawler-conf.yamlfiles (core, opensearch, solr) andconfiguration.adoc; the SSL/TLS and Authentication sections ofextending.adocnow describe the interaction.Behaviour notes
http.trust.everything: true+http.verify.hostnames: falserestores the old permissive combination, but each half now needs its own explicit key and each logs a WARN.Proxy-Authorizationbuilt by the proxy authenticator) is untouched: it goes to the configured proxy, not to crawled servers.Testing
The new
OkHttpTrustEverythingTest(11 tests) serves HTTPS locally with two self-signed PKCS12 keystores — one issued forlocalhost, one for another host name — and covers:trustAllContextUsesTls— the static context is aTLScontext;selfSignedCertificateRejectedByDefault— the new default rejects the handshake;trustAllFetchesServerWithSelfSignedCertificate— the opt-in still crawls such hosts;hostnameIsStillVerified— under trust-all, a certificate for another name is still rejected (this is the regression test from the issue);hostnameVerificationCanBeDisabledSeparately— the separate key works;http.credentials.allow.insecureis set, verified against the server's request journal.mvn -pl core verifypasses: 430 tests, checkstyle, forbiddenapis and google-java-format clean.Release note (required, behaviour change)
The default of
http.trust.everythingchanged fromtruetofalsein this major release. Crawls relying on self-signed or otherwise unvalidatable certificates must set:and, if they authenticate with basic auth, custom credential headers or cookies, additionally:
Hostname verification stays enabled unless
http.verify.hostnames: falseis set explicitly.