Broken Authentication in Koa with Mongodb
Broken Authentication in Koa with Mongodb — how this specific combination creates or exposes the vulnerability
Broken Authentication in a Koa application using Mongodb often arises from a mismatch between session or token handling in Koa and the way credentials are stored and validated in Mongodb. When Koa relies on opaque session identifiers stored server-side (e.g., in memory or a separate store) while Mongodb holds user documents with plaintext or weakly protected credentials, an attacker who gains access to session identifiers or guesses predictable values can hijack authenticated sessions.
Common patterns that lead to vulnerabilities include:
- Storing passwords in Mongodb without strong, adaptive hashing (e.g., using unsalted SHA-1 or MD5), making offline credential recovery feasible if the database is compromised.
- Using JWTs signed with weak algorithms (such as
noneor symmetric HS256 with a weak secret) and failing to validate issuer/audience, allowing token substitution across users. - Missing binding between a Koa session identifier and the Mongodb user’s record (e.g., no mapping between session ID and user ID), enabling horizontal privilege escalation if an attacker can guess another user’s session ID.
- Inadequate rate limiting on Koa authentication endpoints, enabling online brute-force or credential-stuffing attacks against Mongodb-stored accounts.
Because middleBrick tests unauthenticated attack surfaces across Authentication and BOLA/IDOR, it can detect indicators such as weak token handling, predictable resource identifiers, and missing ownership checks that map to this vulnerability in the Koa + Mongodb context.
Mongodb-Specific Remediation in Koa — concrete code fixes
To address Broken Authentication when using Koa with Mongodb, apply strong credential storage, secure token practices, and explicit access controls. Below are concrete, idiomatic examples.
Secure password storage with bcrypt
Store passwords using an adaptive one-way function. Never store plaintext or fast hashes in Mongodb.
// user.model.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
passwordHash: { type: String, required: true }
});
userSchema.pre('save', async function (next) {
if (!this.isModified('passwordHash')) return next();
const saltRounds = 12;
this.passwordHash = await bcrypt.hash(this.passwordHash, saltRounds);
next();
});
userSchema.methods.comparePassword = async function (candidate) {
return bcrypt.compare(candidate, this.passwordHash);
};
const User = mongoose.model('User', userSchema);
module.exports = User;
Koa login endpoint with strict validation and session binding
Ensure each authenticated session or token is explicitly tied to a single Mongodb user record.
// auth.controller.js
const Router = require('koa-router');
const jwt = require('jsonwebtoken');
const User = require('./user.model');
const router = new Router();
const JWT_SECRET = process.env.JWT_SECRET; // use a strong, rotated secret
router.post('/login', async (ctx) => {
const { email, password } = ctx.request.body;
if (!email || !password) {
ctx.status = 400;
ctx.body = { error: 'Missing credentials' };
return;
}
const user = await User.findOne({ email }).select('+passwordHash');
if (!user || !(await user.comparePassword(password))) {
ctx.status = 401;
ctx.body = { error: 'Invalid credentials' };
return;
}
// Bind token to user ID and include minimal claims
const payload = {
sub: user._id,
email: user.email,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour
};
const token = jwt.sign(payload, JWT_SECRET, { algorithm: 'RS256' });
ctx.cookies.set('access_token', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 3600000
});
ctx.status = 200;
ctx.body = { message: 'Authenticated' };
});
module.exports = router;
Protect routes with user-bound authorization
Always resolve the resource owner in Mongodb and compare it with the token subject before allowing access.
// auth.middleware.js
const jwt = require('jsonwebtoken');
const User = require('./user.model');
const verifyToken = (ctx, next) => {
const token = ctx.cookies.get('access_token');
if (!token) {
ctx.status = 401;
ctx.body = { error: 'Missing token' };
return;
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['RS256'] });
ctx.state.user = decoded; // { sub, email, iat, exp }
return next();
} catch (err) {
ctx.status = 401;
ctx.body = { error: 'Invalid token' };
}
};
const ensureOwnership = async (ctx, next) => {
const user = await User.findById(ctx.state.user.sub);
if (!user) {
ctx.status = 404;
ctx.body = { error: 'User not found' };
return;
}
// Attach full user record for downstream handlers
ctx.state.userRecord = user;
await next();
};
module.exports = { verifyToken, ensureOwnership };
Rate limiting and monitoring
Apply rate limiting at the Koa layer and monitor authentication outcomes to reduce online attacks against Mongodb accounts.
// rate-limits.js
const Ratelimit = require('koa-ratelimit');
const { MongoClient } = require('mongodb');
const client = new MongoClient(process.env.MONGO_URI);
const authLimiter = new Ratelimit({
storeClient: client,
duration: 60000,
max: 5,
disableHeader: false
});
// Apply to login route
router.post('/login', authLimiter.middleware({ key: (ctx) => ctx.ip }), async (ctx) => {
// login logic from above
});
These measures align findings from middleBrick checks (Authentication, BOLA/IDOR, and Unsafe Consumption) by ensuring strong storage, clear ownership mapping, and controlled exposure of authentication-related endpoints.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |