SSL Stripping Attacks: When Missing HSTS Headers Leave Domains Vulnerable
Introduction
An SSL stripping attack requires no zero-day exploits, no malware, and no server compromise. The attacker intercepts the initial unencrypted negotiation between a browser and server, downgrading the connection from HTTPS to HTTP before encryption begins. Positioned on a coffee shop WiFi, compromised router, or rogue cellular tower, the attacker captures everything: login credentials, session tokens, form data, and API keys.
This attack is preventable. A single HTTP security header, HTTP Strict-Transport-Security (HSTS), can make SSL stripping ineffective. Yet thousands of domains remain vulnerable because they omit the header or configure it incorrectly. Misconfiguration can lead to audit failures, legal exposure, and regulatory penalties under frameworks like NIST, PCI-DSS, and industry standards.
This article explains how SSL stripping attacks work, why missing HSTS headers enable the vulnerability, and how to detect and prevent this attack class.
How SSL Stripping Attacks Work
SSL stripping exploits an asymmetry in the web: users expect encryption, but the initial connection is not encrypted.
When a user types example.com into their browser (without the https:// prefix), the browser makes an HTTP request to port 80. The server responds with a 301 or 302 redirect to HTTPS, sent in cleartext. This is where the attacker intercepts:
User Browser Attacker Web Server
| | |
|--- HTTP GET example.com --|--- HTTP GET example.com ---->|
| | |
|<-- 301 Redirect to HTTPS --|<-- 301 Redirect to HTTPS ----|
| |
| | (Attacker intercepts and strips redirect)
|<-- 200 OK (HTTP) ---------|
| |
| (continues over HTTP) |
If the attacker intercepts the redirect and replaces it with a 200 OK response, the browser continues the session unencrypted. The attacker proxies requests to the legitimate HTTPS server and captures all data in transit. The user sees a functional website with no indication that encryption was stripped.
HTTP redirects to HTTPS are optional from the browser's perspective. The attacker's task is to prevent the user from receiving the redirect.
The role of DNS and network position
The attacker typically uses DNS spoofing or ARP poisoning at the network layer, or simply operates on the same local WiFi network. They don't need to crack encryption or compromise the certificate. They only need to prevent the encryption negotiation from starting.
The attack surface: a concrete scenario
Consider an organization running an internal corporate portal at portal.company.local. Employees connect from branch offices, coffee shops, and home networks. An attacker positions themselves on a WiFi network used by corporate travelers.
A user types portal.company.local into their browser. The browser makes an HTTP request. The attacker intercepts and responds with an HTTP 200 OK, serving a proxy of the login page. The user enters credentials. The attacker captures them. Simultaneously, the attacker forwards the request to the legitimate server over HTTPS, receives the authenticated page, and serves it back to the user over HTTP. The user is logged in and suspects nothing.
The attacker now has:
- Username and password in plaintext
- Session cookies
- Any API tokens transmitted during login
- Access to all subsequent requests (changing passwords, accessing financial data, downloading files)
The scenario is catastrophic because the user never established an encrypted channel.
Business impact
For organizations handling sensitive data, SSL stripping attacks lead to:
- Credential theft: Compromised employee accounts, leading to lateral movement and data exfiltration
- Data breaches: Customer or financial data intercepted in transit
- Compliance violations: Failure to protect data in transit under HIPAA, PCI-DSS, GDPR, and SOC 2
- Regulatory penalties: Fines for failure to implement industry-standard security controls
- Reputation damage: Public disclosure of a preventable attack
- Legal liability: Lawsuits from customers whose data was intercepted
How CompliSight detects this vulnerability
CompliSight's WEB_SECURITY validation standard includes automated checks for HSTS configuration to identify domains vulnerable to SSL stripping.
HSTS header validation
When CompliSight analyzes a domain, it checks for the Strict-Transport-Security header and validates its configuration:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The validation checks:
- Header presence: Is HSTS enabled?
- max-age value: Is the duration long enough (ideally ≥ 1 year = 31,536,000 seconds)?
- includeSubDomains directive: Are subdomains covered?
- preload directive: Is the domain submitted to the HSTS preload list?
HSTS preload list verification
CompliSight cross-references domains against the official HSTS preload list to confirm that browsers will automatically upgrade connections. Domains marked with the preload directive but not in the list receive a remediation recommendation.
Misconfiguration detection
CompliSight flags common mistakes:
- HSTS headers sent only on HTTPS (ineffective; should also be sent on HTTP redirects)
- max-age values too short (< 10,800 seconds)
- Absence of
includeSubDomainswhen subdomains host sensitive content - HSTS header sent with invalid formatting
Continuous monitoring
CompliSight's continuous validation detects when HSTS headers are removed or downgraded, alerting administrators before users are exposed.
Practical mitigation and implementation steps
Step 1: Implement HSTS headers
Add the following header to every HTTP and HTTPS response from your web server. HSTS must be sent on both HTTP and HTTPS responses.
Nginx configuration
server {
listen 80;
listen 443 ssl http2;
server_name example.com www.example.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Redirect HTTP to HTTPS
if ($scheme = http) {
return 301 https://$server_name$request_uri;
}
}
Apache configuration
<VirtualHost *:80 *:443>
ServerName example.com
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
<If "%{REQUEST_SCHEME} == 'http'">
Redirect 301 / https://example.com/
</If>
</VirtualHost>
Node.js / Express
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
next();
});
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') \!== 'https' && process.env.NODE_ENV === 'production') {
res.redirect(301, `https://${req.header('host')}${req.url}`);
} else {
next();
}
});
Step 2: Gradually increase max-age
Start with a conservative max-age and increase it over 1-3 months:
- Week 1-2:
max-age=600(10 minutes) - test the header delivery - Week 3-4:
max-age=86400(1 day) - expand duration - Month 2-3:
max-age=2592000(30 days) - further increase - Month 3+:
max-age=31536000(1 year) - production standard
Step 3: Add to HSTS preload list
Submit your domain to the HSTS preload list at https://hstspreload.org. This ensures browsers include your domain hardcoded to use HTTPS, eliminating the first-request vulnerability.
Step 4: Include subdomains
Use the includeSubDomains directive to extend HSTS to all subdomains. This closes the attack surface for services on api.example.com, mail.example.com, and similar.
Step 5: Deploy monitoring
Configure your infrastructure to alert when HSTS headers are missing, malformed, or when max-age values drop below acceptable thresholds. CompliSight's continuous validation automates this.
Key takeaways
- SSL stripping is preventable with HSTS headers
- Thousands of domains lack proper HSTS configuration
- Credential theft, data breaches, and compliance violations are direct consequences
- Implementation is straightforward: add one header to server responses
- Monitor compliance continuously; a single misconfiguration reopens the attack surface
- The HSTS preload list eliminates the first-request window
By implementing HSTS headers correctly and monitoring them continuously, your organization eliminates this attack class and demonstrates to regulators and customers a commitment to data protection.