Skill
broken-auth-detector
OWASP A02 - Broken Authentication Detection. Use this skill when reviewing login systems, session management, password handling, or authentication flows. Activate when: login, authentication, password, session, token, JWT, OAuth, credentials, sign in, logout, remember me, forgot password, password reset, MFA, 2FA.
From security-guardianInstall
1
Run in your terminal$
npx claudepluginhub latestaiagents/agent-skills --plugin security-guardianTool Access
This skill uses the workspace's default tool permissions.
Skill Content
Broken Authentication Detector (OWASP A02)
Identify and fix authentication vulnerabilities including weak passwords, session hijacking, and credential stuffing.
When to Use
- Reviewing login/signup implementations
- Auditing session management
- Implementing password reset flows
- Adding OAuth/SSO integration
- Setting up JWT authentication
- Implementing MFA/2FA
Common Vulnerabilities
| Vulnerability | Risk | Impact |
|---|---|---|
| Weak password policy | HIGH | Easy brute force |
| No rate limiting | HIGH | Credential stuffing |
| Session fixation | HIGH | Account takeover |
| Insecure password storage | CRITICAL | Mass credential theft |
| Missing MFA | MEDIUM | Single point of failure |
| Predictable tokens | CRITICAL | Session hijacking |
Detection Patterns
Weak Password Handling
// VULNERABLE - Plain text password storage
user.password = req.body.password;
await user.save();
// VULNERABLE - Weak hashing
const hash = md5(password);
const hash = sha1(password);
// VULNERABLE - No salt
const hash = bcrypt.hashSync(password); // Missing salt rounds
// VULNERABLE - Weak password policy
if (password.length >= 4) { // Too short!
// accept password
}
Session Vulnerabilities
// VULNERABLE - Predictable session ID
const sessionId = `session_${Date.now()}`;
const sessionId = `user_${userId}`;
// VULNERABLE - Session in URL
res.redirect(`/dashboard?sessionId=${sessionId}`);
// VULNERABLE - No session expiry
req.session.cookie.maxAge = null; // Never expires
// VULNERABLE - Session not invalidated on logout
app.post('/logout', (req, res) => {
res.redirect('/login'); // Session still valid!
});
JWT Vulnerabilities
// VULNERABLE - No algorithm verification
const decoded = jwt.decode(token); // Doesn't verify!
// VULNERABLE - Weak secret
const token = jwt.sign(payload, 'secret123');
// VULNERABLE - Algorithm confusion
const decoded = jwt.verify(token, secret); // Accepts 'none' algorithm
// VULNERABLE - No expiration
const token = jwt.sign({ userId }, secret); // No exp claim
Secure Implementation
1. Password Hashing
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
// Hash password
async function hashPassword(password) {
return bcrypt.hash(password, SALT_ROUNDS);
}
// Verify password
async function verifyPassword(password, hash) {
return bcrypt.compare(password, hash);
}
// Usage
const hashedPassword = await hashPassword(req.body.password);
user.password = hashedPassword;
await user.save();
# Python with argon2 (recommended)
from argon2 import PasswordHasher
ph = PasswordHasher()
# Hash
hashed = ph.hash(password)
# Verify
try:
ph.verify(hashed, password)
except VerifyMismatchError:
raise InvalidCredentials()
2. Strong Password Policy
const passwordPolicy = {
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecial: true,
preventCommon: true,
preventUserInfo: true
};
function validatePassword(password, userInfo = {}) {
const errors = [];
if (password.length < 12) {
errors.push('Password must be at least 12 characters');
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain uppercase letter');
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain lowercase letter');
}
if (!/[0-9]/.test(password)) {
errors.push('Password must contain a number');
}
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
errors.push('Password must contain a special character');
}
// Check against common passwords
if (COMMON_PASSWORDS.includes(password.toLowerCase())) {
errors.push('Password is too common');
}
// Check if contains user info
const userValues = Object.values(userInfo).map(v => v?.toLowerCase());
if (userValues.some(v => password.toLowerCase().includes(v))) {
errors.push('Password cannot contain personal information');
}
return { valid: errors.length === 0, errors };
}
3. Secure Session Management
const session = require('express-session');
const RedisStore = require('connect-redis').default;
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET, // Strong, random secret
name: '__Host-sessionId', // Secure cookie prefix
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // HTTPS only
httpOnly: true, // No JavaScript access
sameSite: 'strict', // CSRF protection
maxAge: 3600000, // 1 hour
domain: 'example.com',
path: '/'
},
genid: () => crypto.randomUUID() // Cryptographically random
}));
// Regenerate session on login
app.post('/login', async (req, res) => {
const user = await authenticateUser(req.body);
// Regenerate session ID to prevent fixation
req.session.regenerate((err) => {
req.session.userId = user.id;
req.session.loginTime = Date.now();
res.redirect('/dashboard');
});
});
// Destroy session on logout
app.post('/logout', (req, res) => {
req.session.destroy((err) => {
res.clearCookie('__Host-sessionId');
res.redirect('/login');
});
});
4. Secure JWT Implementation
const jwt = require('jsonwebtoken');
const JWT_CONFIG = {
secret: process.env.JWT_SECRET, // 256+ bit random string
algorithm: 'HS256',
expiresIn: '15m', // Short-lived access tokens
issuer: 'your-app',
audience: 'your-app-users'
};
// Generate token
function generateAccessToken(user) {
return jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role
},
JWT_CONFIG.secret,
{
algorithm: JWT_CONFIG.algorithm,
expiresIn: JWT_CONFIG.expiresIn,
issuer: JWT_CONFIG.issuer,
audience: JWT_CONFIG.audience
}
);
}
// Verify token (with algorithm enforcement)
function verifyToken(token) {
return jwt.verify(token, JWT_CONFIG.secret, {
algorithms: [JWT_CONFIG.algorithm], // IMPORTANT: Whitelist algorithms
issuer: JWT_CONFIG.issuer,
audience: JWT_CONFIG.audience
});
}
// Refresh token (longer-lived, stored securely)
function generateRefreshToken(user) {
const token = crypto.randomBytes(64).toString('hex');
// Store in database with user association and expiry
return token;
}
5. Rate Limiting
const rateLimit = require('express-rate-limit');
// Login rate limiting
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per window
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => req.body.email || req.ip, // Rate limit by email
handler: (req, res) => {
// Log failed attempts for monitoring
logger.warn('Rate limit exceeded', {
ip: req.ip,
email: req.body.email
});
res.status(429).json({ error: 'Too many attempts' });
}
});
app.post('/login', loginLimiter, loginHandler);
// Account lockout after repeated failures
async function handleFailedLogin(email) {
const key = `failed_login:${email}`;
const attempts = await redis.incr(key);
await redis.expire(key, 900); // 15 minutes
if (attempts >= 5) {
await lockAccount(email);
await sendLockoutNotification(email);
}
}
6. Multi-Factor Authentication
const speakeasy = require('speakeasy');
const qrcode = require('qrcode');
// Setup MFA
async function setupMFA(user) {
const secret = speakeasy.generateSecret({
name: `YourApp:${user.email}`,
length: 32
});
// Store secret (encrypted) in database
user.mfaSecret = encrypt(secret.base32);
user.mfaEnabled = false; // Enable after verification
await user.save();
// Generate QR code
const qrCodeUrl = await qrcode.toDataURL(secret.otpauth_url);
return { qrCodeUrl, manualKey: secret.base32 };
}
// Verify MFA token
function verifyMFA(user, token) {
const secret = decrypt(user.mfaSecret);
return speakeasy.totp.verify({
secret: secret,
encoding: 'base32',
token: token,
window: 1 // Allow 1 step tolerance
});
}
Code Review Checklist
- Passwords hashed with bcrypt/argon2 (cost factor >= 12)
- Session IDs are cryptographically random
- Sessions regenerated after login
- Sessions destroyed on logout
- Cookies have Secure, HttpOnly, SameSite flags
- JWT algorithms explicitly whitelisted
- JWT tokens have short expiration
- Rate limiting on authentication endpoints
- Account lockout after failed attempts
- Password reset tokens are single-use and expire
- MFA option available for users
- No credentials in URLs or logs
Best Practices
- Use established libraries: Don't roll your own crypto
- Implement MFA: Especially for sensitive accounts
- Monitor authentication: Log and alert on anomalies
- Secure password reset: Time-limited, single-use tokens
- Credential stuffing protection: Rate limiting + CAPTCHA
- Session timeout: Both idle and absolute timeouts
Similar Skills
Stats
Parent Repo Stars2
Parent Repo Forks0
Last CommitFeb 5, 2026