Skip to content
TLS Configuration

TLS Misconfiguration, Explained: Old Protocols, Broken Certificates and the Hop That Drops HTTPS

A padlock in the address bar does not prove the channel is sound. Here is how legacy TLS versions, weak cipher suites, faulty certificates, cleartext redirect hops and missing HSTS expose traffic to anyone on the path, and a configuration that closes them.

Part 1 of 1From the TLS Configuration series

At a glance

CWE-295
Interpreter
TLS stacks in clients, servers and proxies, and the browser following redirects
Required condition
A TLS endpoint accepts legacy versions or weak suites, presents a faulty certificate, or a redirect or first request travels over plain HTTP.
Potential impact
Anyone on the network path can read or rewrite traffic, steal session cookies or strip HTTPS entirely.
Primary defense
TLS 1.2 and 1.3 with AEAD suites, an automatically renewed full-chain certificate, HTTPS on every hop and HSTS.

Safe practice: use benign proof only, in a disposable local lab or on a system you are explicitly authorized to test.

On this page

HTTPS protects a request only if every part of the channel holds: the protocol version, the cipher suite, the certificate, and every redirect between the clicked link and the page that answers. TLS misconfiguration is a gap anywhere in that chain, usually behind a padlock that still looks fine.

OWASP Top 10:2025 lists a site that does not enforce TLS on every page, or still supports weak encryption, under A04 Cryptographic Failures, and maps cleartext transmission (CWE-319) and inadequate encryption strength (CWE-326) to that category. These flaws do not hand over the server; they hand over the traffic.

Weakness Where it lives What it costs
TLS 1.0 or 1.1 accepted Server or load balancer TLS settings Legacy constructions no current client needs
Weak cipher suite (RC4, 3DES, NULL, EXPORT) Cipher list Traffic that can be decrypted, or was never encrypted
Expired, mismatched or self-signed certificate Certificate deployment Warnings users learn to click through
Missing intermediate certificates Certificate file Validation failures in clients that do not fetch the chain
HTTPS-to-HTTP redirect hop Redirect rules and proxies Cookies and URLs in cleartext on that hop
No HSTS Response headers The first http:// request can be intercepted and kept on HTTP

How it happens

Most TLS weaknesses are inherited rather than chosen. A load balancer keeps a compatibility policy picked years ago, or a configuration is copied from a guide that predates TLS 1.3. A certificate is renewed by hand and deployed as cert.pem instead of fullchain.pem, so the server sends the leaf without its intermediates. Browsers often fill in the missing intermediate, so the site looks fine while API clients fail.

Cleartext hops usually come from TLS termination. A load balancer handles HTTPS and forwards plain HTTP to nginx on port 80. When someone requests a directory without its trailing slash, nginx answers with a redirect built from the scheme it received:

HTTP/1.1 301 Moved Permanently
Location: http://app.example/docs/

Application code does the same when it assembles absolute URLs from the request scheme without trusting the proxy's X-Forwarded-Proto. The port 80 listener then redirects back to HTTPS, so nobody notices the cleartext hop.

A concrete test: versions, certificate, redirects

An authorized tester checks each layer separately, starting with one handshake per protocol version:

openssl s_client -connect app.example:443 -servername app.example -tls1_1 -cipher 'DEFAULT:@SECLEVEL=0' </dev/null

OpenSSL 3 will not offer TLS 1.0 or 1.1 at its default security level, which is what the -cipher argument lowers. A completed handshake that reports TLSv1.1 means the server accepts the version; a protocol version alert means it refuses. A client that cannot offer the version at all proves nothing about the server.

Next the certificate the server presents for that name:

openssl s_client -connect app.example:443 -servername app.example -showcerts </dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

Compare notAfter with today, the subject alternative names with the requested host, and the issuer with the subject: identical means self-signed. If the Certificate chain block that -showcerts prints holds only entry 0, the leaf, for a certificate issued by a public CA, the intermediates are missing.

Finally the redirects: curl -sI https://app.example/docs. A Location that starts with http:// is the downgrade, whatever the next hop does.

What an attacker gains

Each of these needs a position on the network path, such as shared Wi-Fi or a compromised router:

  • Session theft on a cleartext hop: the browser sends the host's cookies not marked Secure with the http:// request; see security headers for cookie attributes.
  • HTTPS stripping: without HSTS, a typed hostname or old bookmark starts on HTTP, and the attacker keeps the victim there while talking HTTPS to you.
  • Interception behind a warning: users trained by an expired or self-signed certificate click past the warning an attacker's certificate produces too.
  • Decryption or tampering: NULL and EXPORT suites give little or no protection, RC4 has exploitable biases, and BEAST targeted CBC suites in TLS 1.0. RFC 8996 deprecated TLS 1.0 and 1.1 in 2021.

Reading a SelfSec finding

SSL/TLS Analysis is passive and runs on every crawled https:// URL:

  • It probes TLS 1.0, 1.1, 1.2 and 1.3 with one handshake per version its own TLS stack can initiate, cached per host and port for five minutes. Findings attach to each URL, so one server change clears a whole group.
  • SSL 2.0 and 3.0 cannot be initiated by the platform and are never assessed, and neither are TLS 1.0 and 1.1 where the scanning host's own TLS library has them disabled, so a missing deprecated-version finding is not proof; confirm with the openssl test above. A handshake that ends without a clear accept or refuse, such as a timeout, produces an informational "TLS protocol assessment incomplete" finding, not a pass.
  • Only the suite negotiated in each accepted handshake is checked, not every suite the server offers, so a weak suite chosen for other clients can still exist.
  • Expired, not-yet-valid and hostname-mismatched certificates are high. TLS 1.0 or 1.1, weak or sub-128-bit suites, self-signed certificates, incomplete chains, SHA-1 or MD5 signatures and RSA keys under 2048 bits are medium. TLS 1.2 without TLS 1.3, expiry within 30 days and a missing HSTS header are low.
  • Security Headers reports a missing HSTS header as well, at its own severity, and also grades a weak max-age or a missing includeSubDomains; see security headers.

Redirect Chain Analysis reads the 3xx Location hops the browser crawl already followed, sending nothing new. An HTTPS-to-HTTP hop is a medium CWE-319 finding with evidence such as https://app.example -> http://app.example, reported once per host pair. A redirect loop is a low CWE-834 finding. Meta refresh and script redirects are not part of the chain, and http:// resource references inside pages are reported under information disclosure.

The fix: one strict endpoint, HTTPS on every hop

Where nginx terminates TLS:

server {
    listen 80;
    server_name app.example;
    return 301 https://app.example$request_uri;
}

server {
    listen 443 ssl;
    server_name app.example;

    ssl_certificate /etc/letsencrypt/live/app.example/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/app.example/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_prefer_server_ciphers off;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

fullchain.pem holds the leaf followed by its intermediates. ssl_ciphers governs TLS 1.2; every TLS 1.3 suite is already AEAD. The port 80 server redirects to a fixed host rather than $host, which would echo the client's Host header; see host header injection. If nginx sits behind a load balancer that terminates TLS, add absolute_redirect off; so its own trailing-slash redirects send a relative Location.

In ASP.NET Core, pin Kestrel's versions when it terminates TLS, and trust the forwarded scheme when a proxy does:

using System.Net;
using System.Security.Authentication;
using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrel =>
    kestrel.ConfigureHttpsDefaults(https =>
        https.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13));
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.10"));
});
builder.Services.AddHttpsRedirection(options =>
{
    options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
    options.HttpsPort = 443;
});
builder.Services.AddHsts(options =>
{
    options.MaxAge = TimeSpan.FromDays(365);
    options.IncludeSubDomains = true;
});

var app = builder.Build();
app.UseForwardedHeaders();
app.UseHsts();
app.UseHttpsRedirection();

UseForwardedHeaders comes first so the redirect and HSTS middleware see the original HTTPS scheme; list your real proxy addresses. Kestrel takes cipher suites from the operating system's TLS library, so restrict them there.

For certificates, use an ECDSA P-256 or at least 2048-bit RSA key with a SHA-256 signature, and automate renewal with an ACME client: CA/Browser Forum rules cap new public certificates at 200 days from March 15, 2026, falling to 47 days in 2029.

Fixes that do not hold

  • Redirecting HTTP to HTTPS without HSTS: the first request still travels in cleartext.
  • Sending HSTS over HTTP: browsers ignore the header unless it arrives over a valid HTTPS connection.
  • Fixing the origin while the edge terminates TLS: clients negotiate with the CDN or load balancer.
  • Keeping TLS 1.0 for old clients: Chrome and Firefox dropped it in 2020; the clients left are usually scripts and devices that need upgrading.
  • Disabling verification to silence an error: verify=False or rejectUnauthorized: false in your own clients turns a certificate problem into CWE-295 on the client side.
  • Preloading HSTS before every subdomain serves HTTPS: removal from browser preload lists takes months.

A developer review checklist

  1. Check every TLS terminator: CDN, load balancer, ingress and origin.
  2. Offer only TLS 1.2 and 1.3, with ECDHE key exchange and AES-GCM or ChaCha20-Poly1305 for TLS 1.2.
  3. Deploy the full chain, a SHA-256 signature and subject alternative names for every served hostname.
  4. Automate renewal and alert when 30 days remain.
  5. Redirect port 80 permanently to a fixed HTTPS host, and keep application redirects relative or proxy-aware.
  6. Send Strict-Transport-Security: max-age=31536000; includeSubDomains on HTTPS responses.
  7. Rescan and confirm that no redirect chain leaves HTTPS.

References

Module: SSL/TLS Analysis and Redirect Chain Analysis. How it probes: Passive: no payload is sent. For every crawled https:// URL, SSL/TLS Analysis completes a default TLS handshake plus one handshake for each other version among TLS 1.0, 1.1, 1.2 and 1.3 that its own TLS stack can initiate, cached per host and port for five minutes, and reads which versions the server accepts, the cipher suite negotiated in each accepted handshake, and the leaf certificate's validity dates, subject and issuer, subject alternative names, signature algorithm, RSA key size and chain length; the URL's plain GET response is checked for a Strict-Transport-Security header. Redirect Chain Analysis walks the 3xx Location hops the browser crawl already followed and looks for a hop from HTTPS to HTTP and for a redirect loop. How it confirms: Nothing is replayed: the handshake, the certificate and the recorded hops are the evidence. Accepted TLS 1.0 or 1.1, weak or sub-128-bit cipher suites, self-signed certificates, incomplete chains, SHA-1 or MD5 signatures and RSA keys under 2048 bits are medium; expired, not-yet-valid and hostname-mismatched certificates are high; TLS 1.2 without TLS 1.3, expiry within 30 days and a missing HSTS header are low; a lifetime above the CA/Browser Forum maximum and a version whose handshake ended without a clear accept or refuse, such as a timeout, are informational, and versions the scanner cannot initiate, always SSL 2.0 and 3.0, are not assessed rather than assumed absent. An HTTPS-to-HTTP hop is medium and a loop is low, each reported once per host pair. A finding moves from Possible to Confirmed only after this check. Reported as CWE-295, CWE-326, CWE-319, CWE-834 · SARIF 2.1.0.
How SelfSec proves this one
Module
SSL/TLS Analysis and Redirect Chain Analysis
How it probes
Passive: no payload is sent. For every crawled https:// URL, SSL/TLS Analysis completes a default TLS handshake plus one handshake for each other version among TLS 1.0, 1.1, 1.2 and 1.3 that its own TLS stack can initiate, cached per host and port for five minutes, and reads which versions the server accepts, the cipher suite negotiated in each accepted handshake, and the leaf certificate's validity dates, subject and issuer, subject alternative names, signature algorithm, RSA key size and chain length; the URL's plain GET response is checked for a Strict-Transport-Security header. Redirect Chain Analysis walks the 3xx Location hops the browser crawl already followed and looks for a hop from HTTPS to HTTP and for a redirect loop.
How it confirms
Nothing is replayed: the handshake, the certificate and the recorded hops are the evidence. Accepted TLS 1.0 or 1.1, weak or sub-128-bit cipher suites, self-signed certificates, incomplete chains, SHA-1 or MD5 signatures and RSA keys under 2048 bits are medium; expired, not-yet-valid and hostname-mismatched certificates are high; TLS 1.2 without TLS 1.3, expiry within 30 days and a missing HSTS header are low; a lifetime above the CA/Browser Forum maximum and a version whose handshake ended without a clear accept or refuse, such as a timeout, are informational, and versions the scanner cannot initiate, always SSL 2.0 and 3.0, are not assessed rather than assumed absent. An HTTPS-to-HTTP hop is medium and a loop is low, each reported once per host pair.
Reported as CWE-295, CWE-326, CWE-319, CWE-834 · SARIF 2.1.0

A reflected response alone is not enough to promote a TLS Configuration finding. The classical engine has to confirm the behavior before it reaches the report. See the detection engine

SelfSec scanner

Find TLS Configuration in your own app

Reading about the class is one thing. SelfSec tests for it against an application you are authorized to assess, and only calls it real when independent evidence agrees.

Find it Confirmed, not guessed 21 vulnerability modules, with the classical engine owning the verdict. Prove it Evidence you can re-read Every finding keeps the raw request, the response and the baseline it was measured against.
Join the launch list