Web Development

How We Design Secure Login Systems: Best Practices and Patterns

Digiboffins Team
February 12, 202415 min read2100 views
How We Design Secure Login Systems: Best Practices and Patterns

A comprehensive guide to building secure authentication systems. Learn about password hashing, session management, 2FA, and common vulnerabilities.

How We Design Secure Login Systems: Best Practices and Patterns

Introduction

Authentication is the foundation of application security. A single vulnerability can compromise user data and your entire system. Here's how we build secure login systems that protect both users and applications.

Core Security Principles

1. Password Storage

Never Store Plain Text Passwords

Always hash passwords using secure algorithms:

  • bcrypt - Industry standard, slow by design
  • Argon2 - Winner of Password Hashing Competition
  • scrypt - Memory-hard function

Implementation:

const bcrypt = require('bcrypt');
const saltRounds = 12;

// Hashing const hash = await bcrypt.hash(password, saltRounds);

// Verification const isValid = await bcrypt.compare(password, hash);

Key Points:

  • Use salt rounds of 10-12 (higher = slower = more secure)
  • Never reuse salts
  • Never use MD5, SHA1, or SHA256 for passwords

2. Session Management

Secure Session Tokens

Best Practices:

  • Use cryptographically secure random tokens
  • Set appropriate expiration times
  • Implement refresh tokens
  • Store sessions securely (Redis, database)

Example Implementation:

// Generate secure token
const crypto = require('crypto');
const sessionToken = crypto.randomBytes(32).toString('hex');

// Store in Redis with expiration await redis.setex(session:\${sessionToken}, 3600, userId);

Security Measures:

  • HttpOnly cookies (prevent XSS)
  • Secure flag (HTTPS only)
  • SameSite attribute (prevent CSRF)
  • Short expiration times

3. Rate Limiting

Prevent Brute Force Attacks

Implementation:

  • Limit login attempts per IP
  • Implement exponential backoff
  • Use CAPTCHA after failed attempts
  • Account lockout after multiple failures

Example:

// Rate limiting with Redis
const attempts = await redis.incr(login:attempts:\${ip});
if (attempts === 1) {
  await redis.expire(login:attempts:\${ip}, 900); // 15 minutes
}
if (attempts > 5) {
  return res.status(429).json({ error: 'Too many attempts' });
}

4. Two-Factor Authentication (2FA)

Why 2FA?

Adds an extra layer of security even if passwords are compromised.

Implementation Options:

  • TOTP (Time-based One-Time Password) - Google Authenticator, Authy
  • SMS - Less secure but user-friendly
  • Email - Good balance
  • Hardware tokens - Most secure

TOTP Implementation:

const speakeasy = require('speakeasy');

// Generate secret const secret = speakeasy.generateSecret({ name: YourApp (\${user.email}) });

// Verify token const verified = speakeasy.totp.verify({ secret: user.twoFactorSecret, encoding: 'base32', token: userProvidedToken, window: 2 });

5. Password Requirements

Balanced Approach

Don't Over-Complicate:

  • Minimum 8-12 characters
  • Mix of letters, numbers, symbols
  • Avoid overly complex rules

Better Approach:

  • Use password strength meters
  • Encourage passphrases
  • Check against common password lists
  • Implement password history (prevent reuse)

6. OAuth and Social Login

When to Use:

  • Faster user onboarding
  • Reduced password management burden
  • Leverage established security

Implementation:

  • Use established libraries (Passport.js, etc.)
  • Verify tokens properly
  • Handle errors gracefully
  • Don't skip server-side validation

7. Account Recovery

Secure Password Reset

Best Practices:

  • Use time-limited tokens (15-30 minutes)
  • Single-use tokens
  • Send reset links via email (not SMS)
  • Log all reset attempts
  • Require email verification

Implementation:

// Generate reset token
const resetToken = crypto.randomBytes(32).toString('hex');
const resetExpiry = Date.now() + 30 * 60 * 1000; // 30 minutes

await db.users.update({ resetToken: hashToken(resetToken), resetExpiry }, { where: { email } });

// Send email with reset link await sendEmail(email, /reset-password?token=\${resetToken});

Common Vulnerabilities to Avoid

1. SQL Injection

Prevention:

  • Use parameterized queries
  • ORM with built-in protection
  • Input validation

2. Cross-Site Scripting (XSS)

Prevention:

  • Sanitize user input
  • Use Content Security Policy (CSP)
  • HttpOnly cookies

3. Cross-Site Request Forgery (CSRF)

Prevention:

  • CSRF tokens
  • SameSite cookie attribute
  • Verify origin headers

4. Session Fixation

Prevention:

  • Regenerate session ID on login
  • Invalidate old sessions

5. Timing Attacks

Prevention:

  • Constant-time comparison functions
  • Don't reveal if user exists

Security Checklist

  • [ ] Passwords hashed with bcrypt/Argon2
  • [ ] Secure session management
  • [ ] Rate limiting implemented
  • [ ] HTTPS everywhere
  • [ ] 2FA available
  • [ ] Secure password reset
  • [ ] Input validation
  • [ ] SQL injection prevention
  • [ ] XSS protection
  • [ ] CSRF protection
  • [ ] Security headers (CSP, HSTS, etc.)
  • [ ] Regular security audits
  • [ ] Logging and monitoring
  • [ ] Error messages don't leak information

Conclusion

Building secure authentication requires attention to detail and defense in depth. No single measure is sufficient, but together they create a robust security posture. Always stay updated with security best practices and conduct regular security audits.

*Need help securing your authentication system? [Schedule a consultation](/schedule-appointment) with our security experts.*

Stay Ahead in the Digital Gold Rush

Get exclusive insights on building, launching, and scaling digital products. Join our newsletter to get ahead of the curve.

Chat with DigiBoffins

Hi! Click on the WhatsApp icon below to reach our team instantly.

Our team typically replies within a few minutes.

DigiBoffins

Support Team