{"record":{"id":"cc891c20fb914228","repo":"golang/go","slug":"tls-downgrade-attempt-detected-possibly-due-to-a","errorCode":null,"errorMessage":"tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox","messagePattern":"tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/crypto/tls/handshake_client.go","lineNumber":325,"sourceCode":"\tif !ok {\n\t\tc.sendAlert(alertUnexpectedMessage)\n\t\treturn unexpectedMessageError(serverHello, msg)\n\t}\n\n\tif err := c.pickTLSVersion(serverHello); err != nil {\n\t\treturn err\n\t}\n\n\t// If we are negotiating a protocol version that's lower than what we\n\t// support, check for the server downgrade canaries.\n\t// See RFC 8446, Section 4.1.3.\n\tmaxVers := c.config.maxSupportedVersion(roleClient, c.quic != nil)\n\ttls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12\n\ttls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11\n\tif maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||\n\t\tmaxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {\n\t\tc.sendAlert(alertIllegalParameter)\n\t\treturn errors.New(\"tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox\")\n\t}\n\n\tif c.vers == VersionTLS13 {\n\t\ths := &clientHandshakeStateTLS13{\n\t\t\tc:            c,\n\t\t\tctx:          ctx,\n\t\t\tserverHello:  serverHello,\n\t\t\thello:        hello,\n\t\t\tkeyShareKeys: keyShareKeys,\n\t\t\tsession:      session,\n\t\t\tearlySecret:  earlySecret,\n\t\t\tbinderKey:    binderKey,\n\t\t\techContext:   ech,\n\t\t}\n\t\treturn hs.handshake()\n\t}\n\n\ths := &clientHandshakeState{","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_client.go#L307-L343","documentation":"Implements the downgrade sentinel mechanism from RFC 8446 4.1.3. When the client's maximum supported version is TLS 1.3 (or 1.2) but the negotiated version is lower, Go inspects the last 8 bytes of serverHello.random for canary values a legitimate higher-version server deliberately writes (downgradeCanaryTLS12 / downgradeCanaryTLS11). Their presence means the path forced a version lower than the server can actually speak.","triggerScenarios":"Client supports TLS 1.3 but ends up negotiating TLS 1.2 while the server's random contains the 444F574E47724431 (DOWNGRD1) sentinel; or TLS 1.2 negotiated against a TLS 1.2-capable server while the TLS 1.1 canary is present. Occurs when a TLS-intercepting middlebox, broken load balancer, or MitM strips ServerHello supported_versions.","commonSituations":"Corporate TLS inspection / DLP appliance in the path; legacy reverse proxy that terminates TLS 1.2 only; misconfigured ingress that downgrades; genuine MitM attack.","solutions":["Bypass or upgrade the TLS-terminating middlebox / load balancer so TLS 1.3 passes through end-to-end.","Verify the server's real capability with `openssl s_client -connect host:443 -tls1_3` from an unintercepted host.","If you must restrict versions, set Config.MinVersion to what the path actually supports rather than blaming the server.","If nothing is intercepting, treat this as a security incident and investigate the path."],"exampleFix":"// before: pinning MaxVersion can force the downgrade path\ncfg := &tls.Config{MaxVersion: tls.VersionTLS12}\n// after: allow TLS 1.3 and let the server negotiate honestly\ncfg := &tls.Config{MinVersion: tls.VersionTLS12}","handlingStrategy":"try-catch","validationCode":"// You cannot pre-validate a downgrade sentinel without performing the handshake.\n// You can constrain the version policy:\nfunc saneVersionPolicy(cfg *tls.Config) {\n    if cfg.MinVersion == 0 { cfg.MinVersion = tls.VersionTLS12 }\n    // Do not force a MaxVersion below the server's capability.\n}","typeGuard":"func isDowngradeDetected(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"downgrade attempt detected\")\n}","tryCatchPattern":"if _, err := tls.Dial(\"tcp\", addr, cfg); err != nil {\n    if isDowngradeDetected(err) {\n        // Log a security incident; the path is intercepting or downgrading.\n        log.Printf(\"SECURITY: TLS downgrade detected to %s: %v\", addr, err)\n    }\n}","preventionTips":["Do not set Config.MaxVersion below what the server supports.","Audit the network path for TLS-intercepting appliances.","Prefer TLS 1.3 (MinVersion = VersionTLS13)."],"tags":["tls","security","downgrade","mitm","handshake"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}