Skip to Content
OpenVAPTAlpha
Pre-release. Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. Free for your own use, 100% at your own risk. This website collects no data. Read MoreRead Less
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
MediumNET-TLS-001Cryptography and TLSNetwork

Deprecated TLS 1.0 or 1.1 Accepted

Checks whether local TLS services still accept TLS 1.0 or TLS 1.1.

Why It Matters
TLS 1.0 and 1.1 were formally deprecated by RFC 8996 in 2021. They rely on outdated constructions (e.g. SHA-1 in the handshake) and fail PCI DSS and most compliance baselines.
How It Detects
For each local TLS port and site name, attempts a handshake with 127.0.0.1 limited to TLS 1.0 only, then TLS 1.1 only. A completed handshake fails the check.
How to Fix
Apache: SSLProtocol -all +TLSv1.2 +TLSv1.3 . nginx: ssl_protocols TLSv1.2 TLSv1.3; . Postfix/Dovecot: set the minimum protocol to TLSv1.2. Reload the service afterwards.
How to Verify
openssl s_client -connect 127.0.0.1:443 -tls1_1 </dev/null should fail to connect.
CVSS v3.1 Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Control Category
Cryptography and TLS: CIS Controls v8 3; NIST CSF 2.0 PR.DS
Fix Effort
Low
CIS Controls v8 Safeguard
3.10
NIST CSF 2.0 Subcategory
PR.DS-02
OWASP WSTG v4.2
WSTG-CRYP-01
CWE
CWE-326, CWE-327
OWASP Top 10
A02:2021
OWASP ASVS 4.0.3
9.1.3
NIST SP 800-53 Rev. 5
SC-8, SC-13
Benchmark Topic
TLS protocol versions (topic only, no benchmark text)
References
RFC 8996, Deprecating TLS 1.0 and TLS 1.1
NIST SP 800-52 Rev. 2, Guidelines for TLS Implementations
Mozilla Server Side TLS guidelines
Learn the theory behind this check: P4 TLS, Certificates and HTTPS.

Source Code

The complete file that defines this check. Run it with openvapt scan --only NET-TLS-001, or read it offline with openvapt explain NET-TLS-001.

internal/checks/network/tls.go · Lines 1-203View on GitHub at This Commit
1package network
2
3import (
4 "context"
5 "crypto/tls"
6 "fmt"
7 "strings"
8 "time"
9
10 "github.com/openvapt/openvapt/internal/check"
11 "github.com/openvapt/openvapt/internal/checks/collect"
12 "github.com/openvapt/openvapt/internal/sys"
13)
14
15var refTLS = []check.Ref{
16 {Title: "RFC 8996, Deprecating TLS 1.0 and TLS 1.1", URL: "https://www.rfc-editor.org/rfc/rfc8996.html"},
17 {Title: "NIST SP 800-52 Rev. 2, Guidelines for TLS Implementations", URL: "https://csrc.nist.gov/pubs/sp/800/52/r2/final"},
18 {Title: "Mozilla Server Side TLS guidelines", URL: "https://wiki.mozilla.org/Security/Server_Side_TLS"},
19}
20
21// tlsTargets returns (port, SNI name) pairs: port 443 for every local vhost,
22// plus other common TLS ports that are listening, using the first vhost name.
23func tlsTargets(s sys.System) [][2]string {
24 ls, _ := collect.Listeners(s)
25 vh := collect.VHosts(s)
26 var out [][2]string
27 if collect.ListensOn(ls, 443) {
28 for _, v := range vh {
29 out = append(out, [2]string{"443", v})
30 }
31 if len(vh) == 0 {
32 out = append(out, [2]string{"443", "localhost"})
33 }
34 }
35 name := "localhost"
36 if len(vh) > 0 {
37 name = vh[0]
38 }
39 for _, p := range []int{465, 993, 995, 8443, 10000} {
40 if collect.ListensOn(ls, p) {
41 out = append(out, [2]string{fmt.Sprint(p), name})
42 }
43 }
44 return out
45}
46
47func init() {
48 check.Register(&check.Check{
49 ID: "NET-TLS-001",
50 Title: "Deprecated TLS 1.0 or 1.1 Accepted",
51 Domain: check.DomainNetwork,
52 Severity: check.Medium,
53 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N",
54 Summary: "Checks whether local TLS services still accept TLS 1.0 or TLS 1.1.",
55 Rationale: "TLS 1.0 and 1.1 were formally deprecated by RFC 8996 in 2021. They rely on outdated constructions (e.g. SHA-1 in the handshake) and fail PCI DSS and most compliance baselines.",
56 Detection: "For each local TLS port and site name, attempts a handshake with 127.0.0.1 limited to TLS 1.0 only, then TLS 1.1 only. A completed handshake fails the check.",
57 Fix: "Apache: SSLProtocol -all +TLSv1.2 +TLSv1.3 . nginx: ssl_protocols TLSv1.2 TLSv1.3; . Postfix/Dovecot: set the minimum protocol to TLSv1.2. Reload the service afterwards.",
58 Verify: "openssl s_client -connect 127.0.0.1:443 -tls1_1 </dev/null should fail to connect.",
59 Refs: refTLS,
60 Standards: check.Standards{CWE: []string{"CWE-326", "CWE-327"}, OWASP: []string{"A02:2021"}, WSTG: []string{"WSTG-CRYP-01"}, ASVS: []string{"9.1.3"}, NIST: []string{"SC-8", "SC-13"}, CISTopic: "TLS protocol versions"},
61 Run: func(ctx context.Context, s sys.System) []check.Result {
62 targets := tlsTargets(s)
63 if len(targets) == 0 {
64 return []check.Result{check.SkipR("tls", "No local TLS services found")}
65 }
66 var out []check.Result
67 for _, t := range targets {
68 target := t[1] + " on port " + t[0]
69 if _, err := s.TLS(ctx, t[0], t[1], tls.VersionTLS12, tls.VersionTLS13); err != nil {
70 out = append(out, check.IncompleteR(target, "Modern TLS handshake failed, so legacy protocol rejection could not be verified", err.Error()))
71 continue
72 }
73 var weak []string
74 uncertain := false
75 for _, v := range []struct {
76 n string
77 v uint16
78 }{{"TLS 1.0", tls.VersionTLS10}, {"TLS 1.1", tls.VersionTLS11}} {
79 if _, err := s.TLS(ctx, t[0], t[1], v.v, v.v); err == nil {
80 weak = append(weak, v.n+" accepted")
81 } else if ctx.Err() != nil || strings.Contains(strings.ToLower(err.Error()), "timeout") || strings.Contains(strings.ToLower(err.Error()), "connection reset") {
82 uncertain = true
83 }
84 }
85 if len(weak) > 0 {
86 out = append(out, check.FailR(target, "Deprecated TLS versions accepted", weak...))
87 } else if uncertain {
88 out = append(out, check.IncompleteR(target, "A legacy TLS probe ended without a protocol-rejection result"))
89 } else {
90 out = append(out, check.PassR(target, "TLS 1.0 and 1.1 refused"))
91 }
92 }
93 return out
94 },
95 })
96
97 check.Register(&check.Check{
98 ID: "NET-TLS-002",
99 Title: "TLS Certificate Expired or Expiring Soon",
100 Domain: check.DomainNetwork,
101 Severity: check.Medium,
102 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:L",
103 Summary: "Checks the expiry date of the certificate each local TLS service presents.",
104 Rationale: "An expired certificate breaks the site for visitors and trains users to click through warnings, which is exactly what an attacker needs for interception.",
105 Detection: "Performs a normal handshake with 127.0.0.1 using each site name as SNI and reads the leaf certificate's NotAfter date. Expired fails; fewer than 21 days left also fails.",
106 Fix: "Renew the certificate (certbot renew, or your provider's process) and make sure renewal is automated (systemctl list-timers | grep certbot).",
107 Verify: "echo | openssl s_client -connect 127.0.0.1:443 -servername <site> 2>/dev/null | openssl x509 -noout -enddate",
108 Refs: append([]check.Ref{{Title: "Let's Encrypt, certificate lifetimes and renewal", URL: "https://letsencrypt.org/docs/faq/"}}, refTLS[1]),
109 Standards: check.Standards{CWE: []string{"CWE-298"}, OWASP: []string{"A02:2021"}, WSTG: []string{"WSTG-CRYP-01"}, ASVS: []string{"9.2.1"}, NIST: []string{"SC-12", "SC-17"}, CISTopic: "Certificate validity"},
110 Run: func(ctx context.Context, s sys.System) []check.Result {
111 targets := tlsTargets(s)
112 if len(targets) == 0 {
113 return []check.Result{check.SkipR("tls", "No local TLS services found")}
114 }
115 var out []check.Result
116 for _, t := range targets {
117 target := t[1] + " on port " + t[0]
118 info, err := s.TLS(ctx, t[0], t[1], tls.VersionTLS12, tls.VersionTLS13)
119 if err != nil || info.NotAfter.IsZero() {
120 continue
121 }
122 days := int(time.Until(info.NotAfter).Hours() / 24)
123 ev := "notAfter " + info.NotAfter.UTC().Format("2006-01-02")
124 switch {
125 case days < 0:
126 out = append(out, check.FailR(target, "Certificate has expired", ev))
127 case days < 21:
128 out = append(out, check.FailR(target, fmt.Sprintf("Certificate expires in %d days", days), ev))
129 default:
130 out = append(out, check.PassR(target, fmt.Sprintf("Certificate valid for %d more days", days), ev))
131 }
132 }
133 if len(out) == 0 {
134 return []check.Result{check.SkipR("tls", "No certificate could be read")}
135 }
136 return out
137 },
138 })
139
140 check.Register(&check.Check{
141 ID: "NET-TLS-003",
142 Title: "Self-Signed or Mismatched TLS Certificate",
143 Domain: check.DomainNetwork,
144 Severity: check.Medium,
145 CVSS: "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N",
146 Summary: "Checks whether each site presents a self-signed certificate, or one that does not cover its name.",
147 Rationale: "Browsers reject these certificates, so visitors either leave or learn to ignore warnings, which removes protection against interception.",
148 Detection: "Reads the leaf certificate from a loopback handshake per site name. Issuer equal to subject means self-signed; the site name must appear in the certificate's DNS names (wildcards honoured).",
149 Fix: "Issue a certificate from a public CA covering every site name, for example with certbot --apache or certbot --nginx -d example.com -d www.example.com.",
150 Verify: "openssl s_client -connect 127.0.0.1:443 -servername <site> </dev/null | openssl x509 -noout -subject -issuer -ext subjectAltName",
151 Refs: append([]check.Ref{{Title: "RFC 6125, Representation and Verification of Domain-Based Application Service Identity", URL: "https://www.rfc-editor.org/rfc/rfc6125.html"}}, refTLS[1]),
152 Standards: check.Standards{CWE: []string{"CWE-295", "CWE-297"}, OWASP: []string{"A02:2021", "A07:2021"}, WSTG: []string{"WSTG-CRYP-01"}, ASVS: []string{"9.2.1"}, NIST: []string{"SC-17"}, CISTopic: "Certificate trust"},
153 Run: func(ctx context.Context, s sys.System) []check.Result {
154 var out []check.Result
155 for _, t := range tlsTargets(s) {
156 if t[0] != "443" || t[1] == "localhost" {
157 continue
158 }
159 info, err := s.TLS(ctx, t[0], t[1], tls.VersionTLS12, tls.VersionTLS13)
160 if err != nil || info.NotAfter.IsZero() {
161 continue
162 }
163 switch {
164 case info.SelfSigned:
165 out = append(out, check.FailR(t[1], "Certificate is self-signed", "subject "+info.Subject))
166 case !covers(info.DNSNames, t[1]):
167 out = append(out, check.FailR(t[1], "Certificate does not cover this site name", fmt.Sprintf("names %v", info.DNSNames)))
168 case !info.Trusted:
169 out = append(out, check.FailR(t[1], "Certificate chain is not trusted", info.TrustError))
170 default:
171 out = append(out, check.PassR(t[1], "Certificate is CA-issued and covers the site name"))
172 }
173 }
174 if len(out) == 0 {
175 return []check.Result{check.SkipR("tls", "No HTTPS sites found")}
176 }
177 return out
178 },
179 })
180}
181
182func covers(names []string, host string) bool {
183 for _, n := range names {
184 if n == host {
185 return true
186 }
187 if len(n) > 2 && n[:2] == "*." {
188 if i := indexDot(host); i > 0 && host[i:] == n[1:] {
189 return true
190 }
191 }
192 }
193 return false
194}
195
196func indexDot(s string) int {
197 for i := 0; i < len(s); i++ {
198 if s[i] == '.' {
199 return i
200 }
201 }
202 return -1
203}

Also available as plain Markdown for notes, scripts and AI tools. Same facts, same source, no styling.

Cite This Page

If this page helped your work, please cite it. The version and web address are filled in; the access date is today's date in your browser. Every other style, and when credit is required, is on How to Cite OpenVAPT.

APA 7
OpenVAPT. (2026). NET-TLS-001 Deprecated TLS 1.0 or 1.1 Accepted (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/NET-TLS-001.html

In text: (OpenVAPT, 2026). Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

BibTeX
@misc{openvapt_net_tls_001,
  author       = {{OpenVAPT}},
  title        = {{NET-TLS-001 Deprecated TLS 1.0 or 1.1 Accepted}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/NET-TLS-001.html}},
  note         = {OpenVAPT Check Catalogue, version 0.1.0-alpha.6. Accessed 16 October 2026}
}

Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

Citing OpenVAPT does not mean OpenVAPT endorses, approves or has reviewed your work.