Unprotected Transport of Credentials
Description
Unprotected Transport of Credentials occurs when software transmits authentication credentials (passwords, tokens, API keys, session identifiers) over an unencrypted channel. This commonly happens when applications use HTTP instead of HTTPS, send credentials via unencrypted protocols (FTP, Telnet, SMTP without STARTTLS), or embed credentials in URLs. Attackers who can observe network traffic can capture these credentials and impersonate legitimate users.
Risk
Credentials transmitted in plaintext are trivially intercepted through network sniffing, man-in-the-middle attacks, or compromised network infrastructure. This is especially dangerous on public WiFi networks, shared hosting environments, or networks with compromised devices. Once credentials are captured, attackers gain full access to user accounts. Session tokens transmitted unencrypted enable session hijacking. This vulnerability enables account takeover at scale when combined with traffic interception capabilities.
Solution
Always use TLS/HTTPS for transmitting credentials. Enforce HTTPS through HSTS headers and redirect all HTTP to HTTPS. Never include credentials in URLs (they appear in logs and browser history). Use secure, httpOnly cookies for session tokens. Implement certificate pinning for mobile applications. Use encrypted protocols for all services (SFTP, SSH, SMTPS). Configure servers to reject plaintext authentication attempts. Monitor for mixed content that could downgrade security.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Credential Theft Attackers capture plaintext credentials enabling account access. |
| Authentication | Scope: Session Hijacking Intercepted session tokens allow immediate account takeover. |
| Integrity | Scope: Account Compromise Stolen credentials enable unauthorized actions as the victim. |
Example Code + Solution Code
Vulnerable Code
# VULNERABLE: HTTP for authentication
import requests
def login_vulnerable(username, password):
# Credentials sent over HTTP!
response = requests.post(
'http://api.example.com/login', # Not HTTPS!
data={'username': username, 'password': password}
)
return response.json()
# VULNERABLE: Credentials in URL
def api_call_vulnerable(api_key):
# API key exposed in URL (logged, cached, in browser history)!
response = requests.get(
f'http://api.example.com/data?api_key={api_key}'
)
return response.json()
# VULNERABLE: Basic auth over HTTP
def fetch_data_vulnerable(username, password):
response = requests.get(
'http://api.example.com/data',
auth=(username, password) # Basic auth over HTTP!
)
return response.json()
# VULNERABLE: FTP for file transfer
from ftplib import FTP
def upload_vulnerable(host, username, password, file):
# Unencrypted FTP!
ftp = FTP(host)
ftp.login(username, password) # Plaintext credentials!
ftp.storbinary(f'STOR {file}', open(file, 'rb'))
ftp.quit()
# VULNERABLE: Unencrypted email with credentials
import smtplib
def send_email_vulnerable(smtp_host, username, password, message):
# Plaintext SMTP!
server = smtplib.SMTP(smtp_host, 25)
server.login(username, password) # Credentials in plaintext!
server.send_message(message)
server.quit()
// VULNERABLE: Java HTTP client
public class VulnerableClient {
public String login(String username, String password) throws Exception {
// HTTP connection - credentials exposed!
URL url = new URL("http://api.example.com/login");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
String data = "username=" + URLEncoder.encode(username, "UTF-8") +
"&password=" + URLEncoder.encode(password, "UTF-8");
try (OutputStream os = conn.getOutputStream()) {
os.write(data.getBytes(StandardCharsets.UTF_8));
}
return readResponse(conn);
}
// VULNERABLE: Credentials in URL
public String fetchData(String apiKey) throws Exception {
// API key in URL - visible in logs!
URL url = new URL("http://api.example.com/data?api_key=" + apiKey);
return readUrl(url);
}
}
// VULNERABLE: Ignoring certificate validation
public class InsecureSSLClient {
public void connect() throws Exception {
// Disable SSL verification - MitM possible!
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null; }
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
}
// VULNERABLE: Spring endpoint without HTTPS
@RestController
public class VulnerableController {
// No transport security enforced
@PostMapping("/login")
public ResponseEntity<String> login(
@RequestParam String username,
@RequestParam String password) {
// Accepts credentials over HTTP!
return ResponseEntity.ok(authenticate(username, password));
}
}
// VULNERABLE: HTTP API calls
async function loginVulnerable(username, password) {
// HTTP - credentials exposed!
const response = await fetch('http://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
return response.json();
}
// VULNERABLE: Credentials in URL
function fetchDataVulnerable(apiKey) {
// API key in URL!
return fetch(`http://api.example.com/data?api_key=${apiKey}`)
.then(res => res.json());
}
// VULNERABLE: WebSocket without TLS
function connectWebSocketVulnerable(token) {
// Unencrypted WebSocket!
const ws = new WebSocket(`ws://api.example.com/ws?token=${token}`);
return ws;
}
// VULNERABLE: Node.js with disabled SSL verification
const https = require('https');
const agent = new https.Agent({
rejectUnauthorized: false // Disables certificate validation!
});
async function fetchInsecure(url) {
return fetch(url, { agent });
}
// VULNERABLE: Express without HTTPS redirect
const express = require('express');
const app = express();
app.post('/login', (req, res) => {
// No HTTPS enforcement!
const { username, password } = req.body;
// ...
});
// VULNERABLE: Setting cookies without Secure flag
app.post('/login', (req, res) => {
const token = generateToken(req.body.username);
// Cookie without Secure flag - sent over HTTP!
res.cookie('session', token, { httpOnly: true });
res.json({ success: true });
});
Fixed Code
# SAFE: HTTPS for all credential transmission
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
import ssl
def login_safe(username, password):
"""Always use HTTPS for credentials."""
response = requests.post(
'https://api.example.com/login', # HTTPS!
data={'username': username, 'password': password},
verify=True # Verify SSL certificate
)
return response.json()
# SAFE: API key in header, not URL
def api_call_safe(api_key):
"""Credentials in header, over HTTPS."""
response = requests.get(
'https://api.example.com/data',
headers={'Authorization': f'Bearer {api_key}'} # In header!
)
return response.json()
# SAFE: Basic auth over HTTPS
def fetch_data_safe(username, password):
response = requests.get(
'https://api.example.com/data', # HTTPS!
auth=(username, password)
)
return response.json()
# SAFE: SFTP instead of FTP
import paramiko
def upload_safe(host, username, key_path, file):
"""Use SFTP with key authentication."""
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
# Load known hosts
ssh.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
# Use key authentication
private_key = paramiko.RSAKey.from_private_key_file(key_path)
ssh.connect(host, username=username, pkey=private_key)
sftp = ssh.open_sftp()
sftp.put(file, f'/uploads/{os.path.basename(file)}')
sftp.close()
ssh.close()
# SAFE: TLS for email
import smtplib
import ssl
def send_email_safe(smtp_host, username, password, message):
"""Use TLS for SMTP."""
context = ssl.create_default_context()
# Option 1: SMTP with STARTTLS
with smtplib.SMTP(smtp_host, 587) as server:
server.starttls(context=context)
server.login(username, password)
server.send_message(message)
# Option 2: SMTPS (implicit TLS)
# with smtplib.SMTP_SSL(smtp_host, 465, context=context) as server:
# server.login(username, password)
# server.send_message(message)
# SAFE: Enforce minimum TLS version
import ssl
import urllib3
class SecureHTTPAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
context = create_urllib3_context(ssl_version=ssl.TLSVersion.TLSv1_2)
kwargs['ssl_context'] = context
return super().init_poolmanager(*args, **kwargs)
session = requests.Session()
session.mount('https://', SecureHTTPAdapter())
// SAFE: Java HTTPS client
public class SecureClient {
public String login(String username, String password) throws Exception {
// HTTPS connection
URL url = new URL("https://api.example.com/login");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
// Optionally enforce TLS version
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
sslContext.init(null, null, new SecureRandom());
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setRequestMethod("POST");
conn.setDoOutput(true);
String data = "username=" + URLEncoder.encode(username, "UTF-8") +
"&password=" + URLEncoder.encode(password, "UTF-8");
try (OutputStream os = conn.getOutputStream()) {
os.write(data.getBytes(StandardCharsets.UTF_8));
}
return readResponse(conn);
}
// SAFE: API key in header
public String fetchData(String apiKey) throws Exception {
URL url = new URL("https://api.example.com/data");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
// API key in Authorization header
conn.setRequestProperty("Authorization", "Bearer " + apiKey);
return readResponse(conn);
}
}
// SAFE: Certificate pinning
public class CertificatePinningClient {
public void connect(String host) throws Exception {
// Expected certificate pin
String expectedPin = "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
URL url = new URL("https://" + host + "/api");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.connect();
// Verify certificate
Certificate[] certs = conn.getServerCertificates();
X509Certificate cert = (X509Certificate) certs[0];
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] publicKeyHash = digest.digest(cert.getPublicKey().getEncoded());
String actualPin = "sha256/" + Base64.getEncoder().encodeToString(publicKeyHash);
if (!actualPin.equals(expectedPin)) {
throw new SSLException("Certificate pin mismatch!");
}
}
}
// SAFE: Spring Security with HTTPS enforcement
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// Require HTTPS for all requests
.requiresChannel(channel -> channel
.anyRequest().requiresSecure()
)
// HSTS header
.headers(headers -> headers
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
)
);
return http.build();
}
}
// SAFE: Secure cookie configuration
@Configuration
public class CookieConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setUseSecureCookie(true); // Only over HTTPS
serializer.setUseHttpOnlyCookie(true); // Not accessible via JS
serializer.setSameSite("Strict"); // CSRF protection
return serializer;
}
}
// SAFE: HTTPS for all requests
async function loginSafe(username, password) {
const response = await fetch('https://api.example.com/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
return response.json();
}
// SAFE: API key in header
function fetchDataSafe(apiKey) {
return fetch('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
}).then(res => res.json());
}
// SAFE: Secure WebSocket
function connectWebSocketSafe(token) {
// WSS = WebSocket Secure
const ws = new WebSocket('wss://api.example.com/ws');
ws.onopen = () => {
// Send token after connection established
ws.send(JSON.stringify({ type: 'auth', token }));
};
return ws;
}
// SAFE: Express with HTTPS enforcement
const express = require('express');
const helmet = require('helmet');
const app = express();
// Force HTTPS in production
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect(301, `https://${req.headers.host}${req.url}`);
}
next();
});
// HSTS header
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));
// SAFE: Secure cookies
app.post('/login', (req, res) => {
const token = generateToken(req.body.username);
res.cookie('session', token, {
httpOnly: true, // Not accessible via JavaScript
secure: true, // Only sent over HTTPS
sameSite: 'strict', // CSRF protection
maxAge: 3600000 // 1 hour
});
res.json({ success: true });
});
// SAFE: Node.js HTTPS server
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.cert'),
minVersion: 'TLSv1.2'
};
https.createServer(options, app).listen(443);
// Redirect HTTP to HTTPS
const http = require('http');
http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
res.end();
}).listen(80);
// SAFE: Certificate pinning in Node.js
const https = require('https');
const crypto = require('crypto');
const EXPECTED_PIN = 'sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=';
function fetchWithPinning(url) {
return new Promise((resolve, reject) => {
const req = https.get(url, (res) => {
const cert = res.socket.getPeerCertificate();
const publicKey = cert.pubkey;
const hash = crypto
.createHash('sha256')
.update(publicKey)
.digest('base64');
if (`sha256/${hash}` !== EXPECTED_PIN) {
reject(new Error('Certificate pin mismatch'));
return;
}
// Process response...
resolve(res);
});
req.on('error', reject);
});
}
Exploited in the Wild
Firesheep (2010)
Tool demonstrated mass session hijacking on unencrypted WiFi networks, forcing widespread HTTPS adoption.
SSL Stripping Attacks
Attackers downgrade HTTPS to HTTP to intercept credentials on public networks.
Government Surveillance
Mass surveillance programs exploited unencrypted credential transmission for intelligence gathering.
Tools to test/exploit
-
Wireshark — capture plaintext credentials.
-
Bettercap — network attacks including SSL stripping.
-
SSLStrip — downgrade HTTPS to HTTP.
-
testssl.sh — test TLS configuration.
CVE Examples
-
CVE-2020-8945 — Credential exposure over unencrypted connection.
-
CVE-2019-3462 — APT package manager HTTP vulnerability.
-
Numerous application-specific credential exposure issues.
References
-
MITRE. "CWE-523: Unprotected Transport of Credentials." https://cwe.mitre.org/data/definitions/523.html
-
OWASP. "Transport Layer Protection Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html