Get a Pentest and security assessment of your IT network.

Cyber Security

Secure User Authentication

TL;DR

Storing usernames (emails) and passwords together in a single hashed field is insecure. This guide explains how to separate them, hash each individually with strong algorithms, and store the hashes securely for better cyber security.

Solution Guide: Separating & Hashing User Credentials

  1. Understand the Risk
    • Combining usernames and passwords before hashing creates a ‘rainbow table’ vulnerability. If one user has a weak password, attackers can potentially compromise other accounts using similar patterns.
    • It makes salt application more difficult and less effective.
  2. Database Schema Change
  3. Modify your database to store usernames (emails) and password hashes in separate columns.

    • Replace the existing single ‘credentials’ field with two fields:
      • username: VARCHAR(255), UNIQUE index recommended.
      • password_hash: VARCHAR(255) or larger, depending on your hashing algorithm (see step 3).
  4. Choose a Strong Hashing Algorithm
  5. Use modern, robust password hashing algorithms. Avoid older algorithms like MD5 and SHA1.

    • Recommended options:
      • bcrypt: A well-established algorithm with automatic salting.
      • Argon2: Considered the most secure option, but may require more computational resources.
      • scrypt: Another strong option, offering good security and performance.
  6. Hashing Passwords on Registration
  7. When a new user registers, hash their password before storing it in the database.

    # Example using Python and bcrypt
    import bcrypt
    
    def hash_password(password):
        salt = bcrypt.gensalt()
        hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
        return hashed_password.decode('utf-8')
    
    # Example usage:
    new_user_password = "MySecurePassword123"
    hashed_password = hash_password(new_user_password)
    print(hashed_password) # Store this in the password_hash column
    
  8. Verifying Passwords on Login
  9. When a user logs in, retrieve their stored password_hash and compare it to the hash of the entered password.

    # Example using Python and bcrypt
    import bcrypt
    
    def verify_password(entered_password, stored_hash):
        return bcrypt.checkpw(entered_password.encode('utf-8'), stored_hash.encode('utf-8'))
    
    # Example usage:
    stored_hash = "$2b$12$EXAMPLEHASHEDPASSWORD"
    user_entered_password = "MySecurePassword123"
    is_valid = verify_password(user_entered_password, stored_hash)
    print(is_valid) # True if the password matches
    
  10. Salting (if not using bcrypt/Argon2)
  11. If your chosen algorithm doesn’t handle salting automatically, you must add a unique salt to each password before hashing.

    • Generate a random salt for each user. Store the salt alongside the hash (e.g., in another database column).
    • Concatenate the salt with the password before hashing: hash(salt + password)
  12. Security Considerations
    • Key Stretching: Algorithms like bcrypt and Argon2 perform key stretching, making brute-force attacks more difficult.
    • Regular Updates: Keep your hashing libraries up to date to benefit from the latest security improvements.
    • Rate Limiting: Implement rate limiting on login attempts to prevent brute-force attacks.
    • Two-Factor Authentication (2FA): Consider adding 2FA for an extra layer of cyber security.
Related posts
Cyber Security

Zip Codes & PII: Are They Personal Data?

Cyber Security

Zero-Day Vulnerabilities: User Defence Guide

Cyber Security

Zero Knowledge Voting with Trusted Server

Cyber Security

ZeroNet: 51% Attack Risks & Mitigation