This is a missing seatbelt warning light, not a car with no brakes
Tenable plugin 98056 fires when a web application serves content over HTTPS but does not emit the Strict-Transport-Security response header. HSTS is a browser directive that tells clients *'from now on, only speak HTTPS to me for the next N seconds.'* Without it, a first-visit user who types example.com in the URL bar hits port 80, and a network attacker with a foothold on that path can intercept or downgrade the initial request (classic *sslstrip* territory). Once the browser has been to the site once with HSTS set, all subsequent requests are HTTPS-only regardless of what the user types.
This is a misconfiguration / hardening finding, not a CVE. There is no exploitable code flaw, no memory corruption, no auth bypass. Tenable rating it Medium (CVSS v3 6.5, VPR 4.8) is defensible under a strict interpretation of confidentiality impact via MitM, but it dramatically overstates the risk for the modern web where HSTS preload, browser HTTPS-First mode (Chrome 90+, Firefox, Safari), and near-universal DNS-over-HTTPS make the sslstrip attack path a narrow edge case. In practice this belongs in the *deploy in the next release* bucket, not the *page the on-call* bucket.
4 steps from start to impact.
Attacker obtains an on-path position
- Layer-2/3 adjacency or upstream routing control
- Ability to intercept and modify TCP streams
- Most user traffic is over cellular or home broadband, not shared Wi-Fi
- Corporate networks use WPA3-Enterprise + 802.1X, making L2 injection non-trivial
- Public Wi-Fi has largely moved to WPA3-SAE or captive-portal segmentation
Victim initiates a plaintext HTTP request to the target
example.com (not https://example.com), click an http:// link, or use a stale bookmark. Modern browsers now default to HTTPS-First — Chrome, Firefox, Safari, and Edge all auto-upgrade to https:// before falling back. This step fails outright on any browser released in the last ~3 years unless the user has explicitly disabled the feature.- Victim uses a URL that resolves to plaintext HTTP first
- Browser does not enforce HTTPS-First / HTTPS-Only
- Chrome HTTPS-First is default since v90 (2021)
- Firefox HTTPS-Only mode widely rolled out
- Safari upgrades known-HTTPS hosts automatically
- Sites on the HSTS preload list are hard-coded in Chromium — no first-hit window at all
Attacker runs sslstrip / MITMproxy to downgrade the connection
hstshijack module rewrite the intercepted HTTP response, replacing https:// links with http:// and proxying the TLS session to the real server. Because HSTS was never set, the browser has no memory that this host should be HTTPS-only, so it accepts the plaintext channel.- Working sslstrip-class tooling
- Site does not appear on Chromium HSTS preload list
- Mixed-content blocking in modern browsers breaks most stripped pages
- Any subresource loaded via absolute
https://URL will still fail to strip - Increasing use of
Upgrade-Insecure-RequestsCSP directive
Attacker harvests credentials or session cookies
Secure flag is set — which independently mitigates this), and any bearer tokens. Impact ends at the session/credential of the single intercepted user; no server-side compromise.- Session cookies not marked
Secure - No token binding / DPoP / mTLS in use
Securecookie flag is default in most modern frameworks- SameSite=Strict/Lax reduces replay surface
- MFA on the application blunts credential-only theft
The supporting signals.
| Finding type | Configuration / hardening gap — not a code-execution CVE |
|---|---|
| Tenable plugin family | Web App Scanning → HTTP Security Header |
| Tenable severity | Medium (CVSS v3 6.5, CVSS v4 6.3, VPR 4.8) |
| CVE mapping | None — this is a WAS check, not a CVE. NVD/MITRE have no record because there is nothing to record. |
| KEV status | Not applicable — no CVE, cannot be listed |
| EPSS | Not applicable — no CVE |
| Exploit tooling | sslstrip, sslstrip2, bettercap hstshijack, mitmproxy — all mature, all public for a decade+ |
| In-the-wild activity | sslstrip-style attacks are rare in enterprise incident data since ~2020; largely displaced by phishing kits and OAuth abuse |
| Prevalence | Very common finding — Qualys SSL Labs data historically shows ~40–60% of TLS-enabled sites lack HSTS |
| First published | Plugin published 2017-03-31, last updated 2025-05-16 |
| Standards | RFC 6797, OWASP HSTS Cheat Sheet |
| Related CAPEC | CAPEC-65 (Sniff Application Code), CAPEC-102, CAPEC-117 (Interception), CAPEC-383, CAPEC-477 |
noisgate verdict.
This is a defense-in-depth header omission, not an exploitable vulnerability — the decisive factor is that successful abuse requires an on-path attacker AND a victim using a browser without HTTPS-First AND a site not on the preload list, a three-way conjunction that describes a vanishing sliver of real traffic. There is no code flaw to weaponize, no CVE to track, and no chain that reaches beyond a single intercepted user's session.
Why this verdict
- No code vulnerability exists — plugin 98056 is a header-presence check, not a flaw detection. There is nothing to patch on the server binary; the fix is a one-line config change.
- Modern browsers already mitigate — Chrome HTTPS-First (default since v90/2021), Firefox HTTPS-Only, Safari auto-upgrade, and the Chromium HSTS preload list eliminate the first-visit downgrade window for the overwhelming majority of users.
- Requires on-path attacker position — an unauthenticated remote adversary cannot exploit this from the internet; they need L2/L3 interception, which itself implies a coffee-shop / rogue-AP scenario or a nation-state adversary who has bigger levers.
- Role multiplier: public marketing site — chain ends in at-most one user's session; blast radius is a single victim, no lateral movement.
- Role multiplier: authentication portal / SSO IdP login page — chain still ends in one user's credential, but if MFA is absent, that one credential could pivot to identity compromise. This is why HSTS + preload is mandatory for IdP login hosts, but even here the friction stack (HTTPS-First + Secure cookies + MFA) makes real-world compromise implausible; the role multiplier does not lift this above LOW.
- Blast radius is capped at per-victim session theft — no server-side compromise, no fleet impact, no supply-chain path. The high-value-role floor rule does not trigger because there is no chain to fleet-scale outcome even on the worst-case role.
Why not higher?
A MEDIUM rating (Tenable's default) would be defensible only in a world where HTTPS-First browsing is not the norm — that world ended around 2022. Every additional MEDIUM finding in a scan queue costs analyst cycles that would be better spent on actual RCEs. Rating this MEDIUM competes for attention with real vulnerabilities.
Why not lower?
IGNORE is wrong because HSTS *is* required for identity provider login pages, banking portals, and any host on the HSTS preload list — omitting it there is a genuine gap. It also fails common compliance checks (PCI DSS 4.0 §4.2, HIPAA reasonable-safeguards interpretations, some FedRAMP baselines), so it cannot be dismissed entirely; auditors will flag it.
What to do — in priority order.
- Add the HSTS header at the edge (reverse proxy / CDN / load balancer) — Configure
Strict-Transport-Security: max-age=31536000; includeSubDomainson nginx/Apache/HAProxy/F5/CloudFront/Cloudflare. Applying at the edge fixes every backend behind it in one change. Per noisgate remediation SLA for LOW, target within the standard release cycle — no mitigation SLA applies. - Submit high-value hostnames to the Chromium HSTS preload list — For login portals, banking, IdP, and admin consoles, register at hstspreload.org after confirming
max-age≥31536000; includeSubDomains; preload. This eliminates the first-visit window entirely for Chrome, Edge, Firefox, and Safari users. Do this alongside the header rollout. - Enforce HTTP→HTTPS 301 redirects and drop port 80 listeners where feasible — A permanent redirect ensures plaintext requests are immediately upgraded. Better still, terminate port 80 handling entirely for API endpoints. Ship in the same change window as the HSTS header.
- Set the
SecureandHttpOnlyflags on all session cookies — Independent of HSTS,Secureprevents cookies from being sent over any plaintext channel — this alone defeats the core impact scenario of plugin 98056. Audit your framework defaults (Django, Rails, Spring, Express) to confirm. - Roll out Content-Security-Policy with
upgrade-insecure-requests— Instructs the browser to auto-upgrade any subresource load to HTTPS, complementing HSTS for legacy pages with hardcodedhttp://asset URLs.
- WAF rules — a WAF sits server-side; it cannot influence a MitM downgrade that happens on the client's network path.
- TLS 1.3 enforcement — the sslstrip attack occurs *before* TLS is negotiated. Cipher / protocol hardening does not close this gap.
- Certificate pinning at the server — HPKP is deprecated and browser-side pinning is now internal-only (Google, Facebook binaries). Not a general control.
- EDR on the server — the attack never touches the server; it's between the client and the network path. Host EDR sees nothing.
Crowdsourced verification payload.
Run from any auditor workstation or CI job that can reach the target HTTPS endpoint. No privileges required. Example: ./check_hsts.sh https://login.example.com
#!/usr/bin/env bash
# check_hsts.sh — Verify Strict-Transport-Security header on a target URL
# Usage: ./check_hsts.sh https://target.example.com
# Exit: 0 PATCHED, 1 VULNERABLE, 2 UNKNOWN
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "UNKNOWN: usage: $0 https://host" >&2
exit 2
fi
if ! command -v curl >/dev/null 2>&1; then
echo "UNKNOWN: curl not installed" >&2
exit 2
fi
# Grab response headers, follow redirects (server may redirect http->https)
HEADERS=$(curl -sSIL --max-time 10 "$TARGET" 2>/dev/null) || {
echo "UNKNOWN: could not fetch $TARGET"
exit 2
}
# Look for Strict-Transport-Security (case-insensitive) on the FINAL response
HSTS_LINE=$(printf '%s\n' "$HEADERS" | awk 'BEGIN{IGNORECASE=1} /^strict-transport-security:/ {print; found=1} END{exit !found}')
RC=$?
if [[ $RC -ne 0 || -z "$HSTS_LINE" ]]; then
echo "VULNERABLE: no Strict-Transport-Security header on $TARGET"
exit 1
fi
# Parse max-age
MAX_AGE=$(printf '%s' "$HSTS_LINE" | grep -oiE 'max-age=[0-9]+' | head -1 | cut -d= -f2)
if [[ -z "${MAX_AGE:-}" ]]; then
echo "VULNERABLE: HSTS header present but no max-age directive: $HSTS_LINE"
exit 1
fi
if (( MAX_AGE < 31536000 )); then
echo "VULNERABLE: HSTS max-age=$MAX_AGE is below 1 year (31536000). Header: $HSTS_LINE"
exit 1
fi
if ! printf '%s' "$HSTS_LINE" | grep -qi 'includeSubDomains'; then
echo "VULNERABLE: HSTS present but missing includeSubDomains. Header: $HSTS_LINE"
exit 1
fi
echo "PATCHED: $HSTS_LINE"
exit 0
If you remember one thing.
Sources
- Tenable plugin 98056 — Missing HTTP Strict Transport Security Policy
- Tenable Community — plugin 98056 discussion
- RFC 6797 — HTTP Strict Transport Security (HSTS)
- OWASP HSTS Cheat Sheet
- Chromium HSTS Preload submission site
- Google — HTTPS-First Mode in Chrome
- Mozilla MDN — Strict-Transport-Security header
- Tenable HTTP Security Header plugin family
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.