{"record":{"id":"ff6316ee2b6ed294","repo":"golang/go","slug":"tls-client-using-inappropriate-protocol-fallback-ff6316","errorCode":null,"errorMessage":"tls: client using inappropriate protocol fallback","messagePattern":"tls: client using inappropriate protocol fallback","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server_tls13.go","lineNumber":137,"sourceCode":"\t\treturn errors.New(\"tls: client used the legacy version field to negotiate TLS 1.3\")\n\t}\n\n\t// Abort if the client is doing a fallback and landing lower than what we\n\t// support. See RFC 7507, which however does not specify the interaction\n\t// with supported_versions. The only difference is that with\n\t// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]\n\t// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,\n\t// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to\n\t// TLS 1.2, because a TLS 1.3 server would abort here. The situation before\n\t// supported_versions was not better because there was just no way to do a\n\t// TLS 1.4 handshake without risking the server selecting TLS 1.3.\n\tfor _, id := range hs.clientHello.cipherSuites {\n\t\tif id == TLS_FALLBACK_SCSV {\n\t\t\t// Use c.vers instead of max(supported_versions) because an attacker\n\t\t\t// could defeat this by adding an arbitrary high version otherwise.\n\t\t\tif c.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {\n\t\t\t\tc.sendAlert(alertInappropriateFallback)\n\t\t\t\treturn errors.New(\"tls: client using inappropriate protocol fallback\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif len(hs.clientHello.compressionMethods) != 1 ||\n\t\ths.clientHello.compressionMethods[0] != compressionNone {\n\t\tc.sendAlert(alertIllegalParameter)\n\t\treturn errors.New(\"tls: TLS 1.3 client supports illegal compression methods\")\n\t}\n\n\ths.hello.random = make([]byte, 32)\n\tif _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {\n\t\tc.sendAlert(alertInternalError)\n\t\treturn err\n\t}\n\n\tif len(hs.clientHello.secureRenegotiation) != 0 {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server_tls13.go#L119-L155","documentation":"RFC 7507 defines TLS_FALLBACK_SCSV (0x5600), a cipher value a client includes to signal an intentional version downgrade. If the server observes it AND the negotiated version (c.vers) is lower than the server's maximum supported version, the downgrade is treated as a possible attack and aborted with an inappropriate_fallback alert. This is a downgrade-attack defense.","triggerScenarios":"The client's cipher suites include TLS_FALLBACK_SCSV while c.vers < server's maxSupportedVersion. E.g., a client retry loop that steps down from TLS 1.3 to TLS 1.2 and tags the retry with FALLBACK_SCSV, hitting a server that genuinely supports TLS 1.3.","commonSituations":"Client libraries that auto-retry with version downgrade + FALLBACK_SCSV (Chrome, Firefox, some HTTP clients); a firewall or middlebox blocking TLS 1.3 so the client falls back; or an active attacker injecting SCSV to probe downgrade behavior.","solutions":["Fix the root cause preventing the higher version (middlebox/firewall blocking TLS 1.3, broken TLS 1.3 implementation on either side)","If the server genuinely only supports the lower version, ensure the client does not send TLS_FALLBACK_SCSV","Verify tls.Config max supported version is what you intend (don't accidentally cap it below the client's offer)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Verify your server supports the version the client wants to fall back from.\nmaxVer := cfg.maxSupportedVersion(roleServer, false) // conceptual\n_ = maxVer // ensure it is not artificially capped","typeGuard":null,"tryCatchPattern":"if err := tlsConn.Handshake(); err != nil {\n    if strings.Contains(err.Error(), \"inappropriate protocol fallback\") {\n        // Likely a middlebox blocking the higher version, or an attack.\n        log.Printf(\"downgrade/fallback rejected from %v: %v\", remote, err)\n    }\n    c.Close()\n    return\n}","preventionTips":["Investigate the network path: middleboxes that block TLS 1.3 cause clients to fall back and trip this guard","Do not silence this alert — it is a downgrade-attack defense","Confirm tls.Config.MaxVersion is not accidentally set below TLS 1.3"],"tags":["tls","go","security","downgrade","rfc7507","fallback-scsv","handshake"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}