Dictionary Attack with Basic Auth
How Dictionary Attack Manifests in Basic Auth
Basic Access Authentication (Basic Auth) transmits credentials as a Base64‑encoded string in the Authorization header. Because the encoding is reversible, an attacker who can sniff traffic or intercept a request instantly obtains the clear‑text username and password pair. When an API relies solely on Basic Auth without additional throttling or account lockout mechanisms, an attacker can launch a dictionary attack by iterating through large lists of common usernames and passwords.
Typical attack flow:
- The attacker builds a wordlist (e.g.,
rockyou.txtor a custom list of likely credentials). - For each entry
user:pass, the attacker computesBase64(user:pass)and sends a request with the headerAuthorization: Basic <encoded>. - If the server responds with
200 OK(or any success status) instead of401 Unauthorized, the credential pair is considered valid. - The process repeats until a match is found or the wordlist is exhausted.
Because Basic Auth does not inherently include any delay, CAPTCHA, or lockout, the only limiting factor is network latency and any rate‑limiting the API might have implemented elsewhere. In many legacy or internally exposed services, Basic Auth is enabled on administrative endpoints (e.g., /admin, /actuator) where a successful breach yields privileged access.
Real‑world examples include CVE‑2021‑3156 (Heap‑based buffer overflow in sudo) where attackers used brute‑force to gain sudo rights, and numerous incidents involving exposed Docker daemons or Kubernetes API servers protected only by Basic Auth.
Basic Auth‑Specific Detection
middleBrick’s unauthenticated black‑box scan includes a dedicated probe for weak or missing authentication controls on Basic Auth endpoints. When the scanner detects an Authorization: Basic header in a response (or observes that the endpoint accepts such a header), it automatically launches a dictionary‑style credential guessing routine using a curated list of common usernames and passwords.
The detection workflow is:
- Identify endpoints that return
401 Unauthorizedwith aWWW-Authenticate: Basic realm="..."header. - Extract the realm (if present) to tailor the guessing context.
- Sequentially submit credential pairs from the internal wordlist, measuring response codes and timing.
- Flag any successful authentication (
200 OKor2xx) as a high‑severity finding. - Additionally, the scanner checks for missing rate‑limiting or account lockout by measuring response times across many attempts; uniform fast responses suggest no throttling.
Because middleBrick requires no agents or credentials, a user simply submits the target URL (e.g., https://api.example.com/internal) and receives a report that includes:
- The specific Basic Auth endpoint tested.
- Whether a valid credential pair was discovered.
- The number of attempts made before success (if any).
- Remediation guidance focused on strengthening the authentication mechanism.
For teams that integrate security into CI/CD, the middleBrick GitHub Action can be configured to fail a build if any Basic Auth endpoint returns a successful dictionary attack, preventing the deployment of weakly protected APIs.
Basic Auth‑Specific Remediation
Since middleBrick only reports findings, remediation must be applied in the service code or infrastructure. Below are concrete, language‑specific examples that address the core issues: lack of credential throttling, use of weak secrets, and transmission over insecure channels.
1. Enforce HTTPS everywhere
Basic Auth credentials are encoded, not encrypted. Serving the API over TLS prevents network eavesdropping.
# Example: Express.js middleware to enforce HTTPS
const express = require('express');
const app = express();
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.redirect(301, `https://${req.get('host')}${req.url}`);
}
next();
});
2. Add rate limiting on Basic Auth attempts
Limit the number of failed authentication attempts per IP or per account within a time window.
# Example: Using express-rate-limit with a custom key generator
const rateLimit = require('express-rate-limit');
const basicAuthLimiter = rateLimit({
windowMs: 5 * 60 * 1000, // 5 minutes
max: 10, // max 10 failed attempts per IP
keyGenerator: (req) => {
// Use the Authorization header if present, else IP
const auth = req.headers.authorization || '';
return auth.startsWith('Basic ') ? auth : req.ip;
},
handler: (req, res) => {
res.status(429).send({ error: 'Too many login attempts, please try again later.' });
}
});
app.use('/sensitive', basicAuthLimiter);
3. Replace Basic Auth with a stronger scheme
Where possible, migrate to OAuth 2.0 Bearer tokens or JWTs, which are not vulnerable to simple replay of credentials.
# Example: Protecting a route with Passport.js JWT strategy
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
passport.use(new JwtStrategy({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET
}, (jwtPayload, done) => {
return User.findById(jwtPayload.sub).then(user => done(null, user)).catch(err => done(err, false));
}));
app.get('/api/data', passport.authenticate('jwt', { session: false }), (req, res) => {
res.json({ message: 'Protected data' });
});
4. Implement account lockout after repeated failures
Track failed attempts per username and temporarily disable the account.
# Pseudocode for a login handler (language‑agnostic)
function handleBasicAuth(req, res, next) {
const credentials = parseBasicAuth(req.headers.authorization);
const { username, password } = credentials;
const user = getUserByUsername(username);
if (!user || !verifyPassword(password, user.passwordHash)) {
incrementFailedAttempts(username);
if (getFailedAttempts(username) >= 5) {
lockAccount(username, lockDurationMinutes: 15);
}
return res.status(401).set('WWW-Authenticate', 'Basic realm="API"').send('Unauthorized');
}
resetFailedAttempts(username);
// proceed with request
}
By combining transport security, rate limiting, account lockout, and preferably moving to token‑based authentication, the effectiveness of a dictionary attack against Basic Auth is reduced from practical to infeasible.
Frequently Asked Questions
Does middleBrick actually try to guess my Basic Auth credentials during a scan?
Authorization: Basic header, it launches a dictionary‑style credential guessing routine using an internal wordlist of common usernames and passwords. The scan reports whether any credential pair succeeded and how many attempts were made, but it never stores or uses the guessed credentials beyond the scan.If I enable rate limiting on my Basic Auth endpoint, will middleBrick still flag a vulnerability?
429 Too Many Requests responses or significantly increased latency, the finding will be downgraded or omitted, depending on the configured thresholds. The scanner’s report will note the presence of rate limiting and its effectiveness against the attempted dictionary attack.