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
Strict-Transport-Security: max-age=31536000; includeSubDomains on 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.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.Secure and HttpOnly flags on all session cookies — Independent of HSTS, Secure prevents 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.upgrade-insecure-requests — Instructs the browser to auto-upgrade any subresource load to HTTPS, complementing HSTS for legacy pages with hardcoded http:// 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.
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 |
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
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.
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