Skip to content

DefaultTLSConfig sends no ALPN and offers TLS 1.0 + RC4 + 3DES #587

Open

Description

@cRestaaa

Summary

DefaultTLSConfig produces a ClientHello that no mainstream client has sent since about 2012: TLS
1.0 and 1.1 offered, 25 cipher suites including RC4 and 3DES, and no ALPN extension at all. CDN bot
scoring treats that handshake as a non-browser client and returns a block page before any HTTP
header is read.

This affects every consumer. httpx, nuclei and katana send a byte-identical hello.

Measured

Loopback TLS server logging tls.ClientHelloInfo, same machine, back to back:

fastdialer consumers Go net/http stdlib
TLS versions 1.3, 1.2, 1.1, 1.0 1.3, 1.2
ALPN none h2, http/1.1
ciphers 25, incl. 0x0005 RC4, 0x000a 3DES 13

Identical output from httpx v1.9.0 and v1.12.0, nuclei v3.9.0, katana v1.6.1 (fastdialer v0.5.4,
v0.5.10, v0.5.6 respectively). DefaultTLSConfig is unchanged from v0.5.4 through v0.5.25 and main.

Confirmed on the wire too, via a public JA4 echo: t13d2511_… (no ALPN, 25 ciphers) versus
t13d1312h1_… after the change below.

Source

fastdialer/default_config.go:27

var DefaultTLSConfig = &tls.Config{
    Renegotiation:      tls.RenegotiateOnceAsClient,
    InsecureSkipVerify: true,
    MinVersion:         tls.VersionTLS10,
    CipherSuites:       getUnsafeCipherSuites(),  // every insecure + every secure suite
}                                                  // NextProtos never set

The missing NextProtos also means HTTP/2 can never be negotiated by any consumer, since ALPN is
how h2 is agreed.

Impact

On one Cloudflare-fronted estate, 11 of 11 hosts returned Cloudflare block pages to stock httpx.
With only these three fields changed, 0 of 11 did, and every response matched a stock Go client
byte for byte, including three genuine 403s that were not block pages. Three hosts serving HTTP 200
were being recorded as blocked.

This is likely the real cause behind httpx#2224, #2384, #2044 and #624, all closed with
"use -tls-impersonate". In #2224 the reporter stated that -tls-impersonate did not help. The
problem is not that the client fails to impersonate a browser. It is that the default handshake
looks a decade old.

Suggested change

MinVersion: tls.VersionTLS12,
CipherSuites: nil,                        // Go's secure defaults
NextProtos: []string{"h2", "http/1.1"},   // or {"http/1.1"} to avoid changing protocol behaviour

Preferably as options rather than new hardcoded values, so scanning genuinely legacy infrastructure
stays possible. Keeping the current values as the default would still solve this for anyone who can
set the option.

On the compatibility cost: the permissive default exists to reach old servers, which is fair. In our
data it buys nothing. Across ~8,600 HTTPS listeners from eight separate estates, zero supported
nothing at or above TLS 1.2. Pre-1.2 does appear (TLS 1.1 on 889 listeners, TLS 1.0 on 615) but
every one of those also offered 1.2 or 1.3. That is one sample, not a universal claim, which is why
configurable beats changing the default outright.

Repro

cfg := &tls.Config{
    Certificates: []tls.Certificate{selfSigned()},
    GetConfigForClient: func(h *tls.ClientHelloInfo) (*tls.Config, error) {
        fmt.Printf("alpn=%v versions=%v ciphers=%d\n", h.SupportedProtos, h.SupportedVersions, len(h.CipherSuites))
        return nil, nil
    },
}

Point any fastdialer consumer at it and compare with a stock net/http client.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions