Poodle Attack in Express with Basic Auth
Poodle Attack in Express with Basic Auth â how this specific combination creates or exposes the vulnerability
The Poodle attack (CVE-2014-3566) exploits weaknesses in SSL 3.0, particularly its use of predictable initialization vectors in CBC-mode ciphersuites. When an Express server supports SSL 3.0 and is configured with Basic Authentication, the combination can expose credentials and session tokens in plaintext if an attacker can force or downgrade connections to SSL 3.0.
In practice, this occurs when an Express server listens on HTTPS but still enables SSL 3.0 alongside TLS protocols. Basic Auth credentials are base64-encoded, not encrypted; they are only protected by the transport layer. If SSL 3.0 is negotiable, an attacker on a network path can perform a man-in-the-middle downgrade, then use the Poodle padding oracle to decrypt captured ciphertext, including the Authorization header sent by the client.
An Express server that does not explicitly disable SSL 3.0 may inherit this risk from the underlying Node.js or OpenSSL configuration. Even when using strong ciphers, enabling SSL 3.0 reintroduces CBC ciphersuites vulnerable to Poodle. Attackers can repeatedly modify requests (e.g., by altering padding and observing error differences in responses) to recover bytes of the encrypted Authorization header. Once recovered, the credentials grant direct access to protected endpoints until the password is rotated.
middleBrick detects this risk as part of its unauthenticated scan by probing supported protocols and ciphers, identifying SSL 3.0 and weak ciphersuites, and correlating findings with endpoints that use Basic Auth. The scanner does not exploit the vulnerability but highlights the presence of SSL 3.0 alongside authentication-sensitive paths, enabling teams to prioritize remediation.
An example of vulnerable Express HTTPS setup that permits SSL 3.0 is:
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
// Vulnerable: SSLv3 is enabled alongside TLS
secureOptions: require('constants').SSL_OP_SSLV3_SERVER,
};
// Basic Auth middleware (vulnerable when SSL 3.0 is allowed)
app.use((req, res, next) => {
const auth = req.headers['authorization'];
if (!auth) {
res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send('Authentication required');
}
const token = Buffer.from(auth.split(' ')[1], 'base64').toString('utf8');
const [user, pass] = token.split(':');
if (user === 'admin' && pass === 'password123') {
return next();
}
res.status(401).send('Invalid credentials');
});
app.get('/api/secure', (req, res) => {
res.json({ secret: 'protected-data' });
});
https.createServer(options, app).listen(8443, () => {
console.log('HTTPS server running on port 8443');
});
Basic Auth-Specific Remediation in Express â concrete code fixes
To mitigate Poodle when using Basic Auth in Express, disable SSL 3.0 and restrict ciphersuites to TLS-only, modern options. This ensures the transport layer remains robust and does not expose encrypted credentials to padding oracle attacks.
Use Node.jsâ secureOptions to explicitly disable SSL 3.0. For OpenSSL-based configurations, avoid SSL_OP_SSLV3_SERVER and prefer SSL_OP_NO_SSLv3. Combine this with a restricted ciphers list that excludes NULL, EXPORT, and CBC ciphers vulnerable to Poodle where possible, and enforce TLS 1.2+.
Additionally, consider migrating away from Basic Auth over TLS for stronger protections (e.g., token-based auth), but if Basic Auth is required, ensure TLS is mandatory and SSL 3.0 is never enabled.
Here is a secure Express HTTPS setup that disables SSL 3.0 and uses modern ciphers:
const https = require('https');
const fs = require('fs');
const express = require('express');
const constants = require('constants');
const app = express();
// Secure options: disable SSL 3.0, prefer TLS 1.2 and 1.3
const secureOptions = constants.SSL_OP_NO_SSLv3 | constants.SSL_OP_NO_SSLv2;
const ciphers = [
'ECDHE-ECDSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-ECDSA-CHACHA20-POLY1305',
'ECDHE-RSA-CHACHA20-POLY1305',
'ECDHE-ECDSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES128-GCM-SHA256',
].join(':');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
secureOptions,
ciphers,
minVersion: 'TLSv1.2',
};
// Basic Auth middleware remains the same, but transport is hardened
app.use((req, res, next) => {
const auth = req.headers['authorization'];
if (!auth) {
res.set('WWW-Authenticate', 'Basic realm="example"');
return res.status(401).send('Authentication required');
}
const token = Buffer.from(auth.split(' ')[1], 'base64').toString('utf8');
const [user, pass] = token.split(':');
if (user === 'admin' && pass === 'password123') {
return next();
}
res.status(401).send('Invalid credentials');
});
app.get('/api/secure', (req, res) => {
res.json({ secret: 'protected-data' });
});
https.createServer(options, app).listen(8443, () => {
console.log('HTTPS server running on port 8443 with SSL 3.0 disabled');
});
middleBrick can validate these configurations by scanning the endpoint and confirming SSL 3.0 is disabled, strong ciphers are enforced, and Basic Auth is present without protocol weaknesses. Teams can integrate the CLI (middlebrick scan <url>) or GitHub Action to fail builds when insecure settings are detected.