VNX-GO-041: Use of deprecated TLS version
Detect Go TLS configuration that sets MinVersion or MaxVersion (or an options field such as TLSMinVersion) to SSL 3.0, TLS 1.0 or TLS 1.1 (by constant, raw protocol number, a local variable or a package-level constant holding one, a Traefik-style string option or an Envoy TlsParameters enum), enabling negotiation of deprecated protocol versions.
| Field | Value |
|---|---|
| Rule ID | VNX-GO-041 |
| Severity | High |
| SARIF level | error |
| Kind | sast |
| Languages | Go |
| CWE | CWE-326 |
| CAPEC | CAPEC-63 |
| ATT&CK | T1068.003 |
| CWSS vector | CWSS:1.0/TI:H/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:H/DI:H/EX:H/EC:N/P:H |
| Tags | tls crypto version |
| Source | internal/sast/rules/vnx-go-041.rego |
Overview
This rule flags tls.Config struct literals or assignments where MinVersion or MaxVersion is set to tls.VersionSSL30, tls.VersionTLS10 or tls.VersionTLS11 (under any import alias, optionally wrapped in uint16()), to the raw protocol numbers 0x0300 to 0x0302 (only in a file that imports a TLS package), to a local variable assigned one of those values a few lines above, or to a package-level const/var declared as one. An options-struct field named like TLSMinVersion or MinTLSVersion set to one of the constants is reported too, as is a string-typed option set to the constant’s name (Traefik’s MinVersion: "VersionTLS10") and an Envoy go-control-plane TlsMinimumProtocolVersion or TlsMaximumProtocolVersion set to TlsParameters_TLSv1_0 or TlsParameters_TLSv1_1. Comparisons such as state.Version == tls.VersionTLS10, switch cases, version-name maps, comments, string literals (including multi-line code-generation templates) and _test.go files are not reported, and a version read from configuration at runtime or passed as a function argument is not followed. Both TLS 1.0 and TLS 1.1 have been formally deprecated by the IETF (RFC 8996, March 2021) and are prohibited by PCI-DSS since June 2018. Allowing these versions permits negotiation of ciphersuites and protocol features that are vulnerable to POODLE, BEAST, CRIME, and other well-documented attacks. This maps to CWE-326: Inadequate Encryption Strength.
Go’s crypto/tls package defaults to tls.VersionTLS12 as MinVersion since Go 1.18, but explicitly setting the minimum to an older version overrides this safe default and creates a regression that may go unnoticed in code review.
Why This Matters
TLS 1.0 and TLS 1.1 rely on cipher constructions and negotiation mechanisms that are no longer considered safe. BEAST (Browser Exploit Against SSL/TLS) exploits a CBC-mode IV predictability issue in TLS 1.0 that allows session cookie decryption. POODLE (Padding Oracle On Downgraded Legacy Encryption) attacks SSLv3 but a variant affects TLS 1.0’s CBC ciphersuites. Both attacks require an active network position (man-in-the-middle or on the same network segment), but coffee-shop Wi-Fi and cloud shared-tenancy environments make this a realistic threat model for web applications.
Beyond direct cryptographic attacks, TLS 1.0 and 1.1 lack support for secure cipher suites available in TLS 1.2 (AEAD ciphers such as AES-GCM and ChaCha20-Poly1305) and TLS 1.3 (which eliminates all non-AEAD options entirely). PCI-DSS 3.2 made TLS 1.2 the minimum mandatory version for all payment processing environments. Major browsers (Chrome, Firefox, Safari, Edge) have removed support for TLS 1.0 and 1.1. Continuing to allow these versions primarily serves legacy clients that represent a negligible fraction of modern traffic while materially increasing risk. MITRE ATT&CK T1068.003 covers exploitation of weakened cryptographic configurations.
What Gets Flagged
The rule fires on tls.Config fields that reference deprecated version constants.
// FLAGGED: MinVersion allows TLS 1.0, BEAST and POODLE variant attacks possible
func createTLSConfig() *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS10, // deprecated, RFC 8996
}
}
// FLAGGED: explicit TLS 1.1 minimum
func serverConfig() *tls.Config {
cfg := &tls.Config{}
cfg.MinVersion = tls.VersionTLS11
return cfg
}
// FLAGGED: MaxVersion capped at TLS 1.1 prevents TLS 1.2/1.3 negotiation
func legacyClientConfig() *tls.Config {
return &tls.Config{
MaxVersion: tls.VersionTLS11,
}
}
// FLAGGED: the weak value travels through a local variable
func listen(addr string) (net.Listener, error) {
minVer := uint16(tls.VersionTLS10)
conf := &tls.Config{MinVersion: minVer}
return tls.Listen("tcp", addr, conf)
}
// SAFE: TLS 1.2 minimum with preferred TLS 1.3
func createTLSConfig(certFile, keyFile string) (*tls.Config, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return &tls.Config{
MinVersion: tls.VersionTLS12, // RFC 8996 compliant minimum
Certificates: []tls.Certificate{cert},
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
},
}, nil
}
Remediation
-
Set
MinVersion: tls.VersionTLS12in alltls.Configstructs for both servers and clients. This is already the Go default since 1.18, but any explicitMinVersionassignment must be updated to avoid overriding the safe default.// SAFE: explicit TLS 1.2 minimum, TLS 1.3 preferred automatically func newTLSConfig() *tls.Config { return &tls.Config{ MinVersion: tls.VersionTLS12, // Go's crypto/tls negotiates TLS 1.3 automatically when both // sides support it, no explicit MaxVersion needed. } } -
Prefer TLS 1.3 only (
MinVersion: tls.VersionTLS13) for internal services and APIs where all clients are under your control. TLS 1.3 eliminates weak cipher suites entirely, mandates forward secrecy, and reduces handshake round-trips.// SAFE: TLS 1.3 only for internal microservice communication func internalServiceTLSConfig() *tls.Config { return &tls.Config{ MinVersion: tls.VersionTLS13, } } -
Audit all
http.Server,http.Transport,grpc.ServerOption, andnet.Listenerusages to ensure notls.Configis instantiated without an explicitMinVersionor with a deprecated version constant. Centralise TLS configuration in a shared package to enforce consistent settings. -
Test TLS configuration with
go testusingcrypto/tlstest helpers, or with external tools such astestssl.shor Qualys SSL Labs, to verify that TLS 1.0 and 1.1 are not negotiated in production.
References
- CWE-326: Inadequate Encryption Strength
- RFC 8996 – Deprecating TLS 1.0 and TLS 1.1
- OWASP Transport Layer Security Cheat Sheet
- CAPEC-63: Simple Script Injection
- MITRE ATT&CK T1068 – Exploitation for Privilege Escalation
- PCI-DSS v3.2 – Migration from SSL and Early TLS
- Go crypto/tls package documentation
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-go-041
// nosec vnx-go-041
Or record an ignore rule: vulnetix ignore add --rule vnx-go-041 --reason "...". See Suppressing findings.