No Strict-Transport-Security (HSTS) header configured
The Express app never enables helmet's HSTS module (or sets Strict-Transport-Security manually), so browsers will not pin the site to HTTPS, leaving users exposed to SSL-stripping/downgrade attacks on subsequent visits.
Helmet is loaded as separate sub-middlewares (helmet.noSniff() and helmet.frameguard()) rather than as the default helmet() bundle, which means HSTS — normally on by default with helmet() — is never installed. There is also no res.setHeader('Strict-Transport-Security', ...) anywhere in the configuration.
/* Security middleware */
app.use(helmet.noSniff())
app.use(helmet.frameguard())
// app.use(helmet.xssFilter()); // = no protection from persisted XSS via RESTful API
app.disable('x-powered-by')
app.use(featurePolicy({
features: {
payment: ["'self'"]
}
}))
For any deployment that terminates TLS in front of this app, the lack of HSTS means the first request a user makes over plaintext HTTP can be intercepted (a classic SSL-strip MITM), and even repeat visits are not protected because no browser memory of the HTTPS-only commitment is established. This matches true-positive criterion #1 (no HSTS header on an HTML-serving app).
- Start the server and request any URL:
curl -I https://<host>/. - Inspect the response headers — no
Strict-Transport-Securityheader is returned. - A man-in-the-middle on the user's first visit (e.g. a hostile Wi-Fi network) can therefore strip TLS and serve plaintext content without the browser refusing because of a prior HSTS pin.
All browser users of the application, especially those on untrusted networks. Without HSTS, SSL-stripping MITM attacks remain viable, exposing session cookies, JWTs in the Authorization header, and form data on every plaintext-reachable request.
The code at lines 186–194 only mounts helmet.noSniff() and helmet.frameguard() as individual sub-middlewares rather than the bundled helmet() that enables HSTS by default; no res.setHeader('Strict-Transport-Security', ...) exists anywhere in configureApp. Therefore no Strict-Transport-Security header is emitted, leaving browsers vulnerable to SSL-stripping on first/subsequent visits. The scope explicitly instructs to treat findings as a real production app, so HSTS absence on an HTML-serving Express app qualifies. The detector's PoC (curl -I showing no HSTS header) accurately reflects runtime behavior.
The Security middleware block in server.ts only registers helmet.noSniff() and helmet.frameguard() (not the default helmet() bundle), so no Strict-Transport-Security header is ever emitted and no manual res.setHeader for HSTS appears anywhere. Exploitation requires the attacker to be in a privileged network position (e.g. hostile Wi‑Fi / on-path MITM) AND the victim must initiate a plaintext request first, which is why AC=H and UI=R, while PR=N because no app-level auth is involved. Successful SSL‑stripping lets the attacker read session cookies / JWTs (some confidentiality loss) and tamper with served HTML/JS for that victim (some integrity loss), but they don't get arbitrary control over what data and there's no availability impact; the harm stays within the same browser↔app authority so Scope is Unchanged.
- CWE-319
- CWE-523
- OWASP A02:2021 Cryptographic Failures
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security