CodingLad
password security

Strengthening User Authentication: Password Security, Hashing, and Brute-Force Protection

Strengthening User Authentication: Password Security, Hashing, and Brute-Force Protection
0 views
12 min read
#password security

Strengthening User Authentication: Password Security, Hashing, and Brute-Force Protection

Every second, thousands of login attempts target systems worldwide. In 2024 alone, over 8.4 billion credential stuffing attacks were recorded—and these numbers continue to grow. The question isn't whether your system will be targeted, but how well it's protected.

User authentication is one of the most critical components of system security. Weak authentication mechanisms can expose systems to credential theft, brute-force attacks, and unauthorized access. In this comprehensive guide, we explore how modern systems secure passwords, defeat common attacks, and protect login services using practical tools and techniques.

What You'll Learn:

  • Why plaintext password storage is catastrophic
  • How password hashing works and why it's essential
  • The critical role of salting in password security
  • How to protect against brute-force attacks using Fail2ban, DenyHost, and TCP Wrappers

1. Why Plaintext Passwords Are Dangerous

Storing passwords in plaintext is one of the most serious security mistakes a system can make. If an attacker gains access to the database—whether through SQL injection, a data breach, insider threat, or misconfigured backups—every user account is immediately compromised.

Real-World Consequences

The LinkedIn Breach (2012):

  • 6.5 million passwords leaked in plaintext
  • Attackers gained access to hashed passwords, but many were using weak MD5 hashing without salt
  • Millions of accounts were compromised

The RockYou Breach (2009):

  • 32 million passwords stored in plaintext
  • Database was completely exposed
  • Attackers published the entire password list online

Why This Happens

Common reasons systems store passwords in plaintext:

  • Ignorance: Developers may not understand security best practices
  • Convenience: Easier to debug and support users who forget passwords
  • Legacy systems: Older applications that haven't been updated
  • Poor security culture: Organizations that don't prioritize security

Best Practice: Passwords should never be stored directly. Instead, systems store hashes of passwords—one-way cryptographic functions that make it computationally infeasible to recover the original password.


2. Password Hashing Explained

What Is a Hashed Password?

A hash function is a one-way cryptographic function that transforms a password into a fixed-length string of characters. The same input always produces the same output, but the reverse operation is computationally infeasible.

The Authentication Process:

  1. User registers with a password (e.g., "MySecurePass123!")
  2. System hashes the password: hash("MySecurePass123!") → "a3f5b8c2d1e4f6..."
  3. The hash is stored in the database, never the plaintext password
  4. When the user logs in:
    • The entered password is hashed using the same algorithm
    • The generated hash is compared with the stored hash
    • Access is granted only if the hashes match

Mathematical Representation:

password → hash(password)

Key Properties of Hash Functions

1. One-Way Function (Preimage Resistance):

  • Once hashed, the original password cannot be retrieved
  • Even if an attacker steals the hash, the plaintext password is not directly revealed
  • You can verify a password by hashing it and comparing, but you can't "unhash"

2. Deterministic:

  • Same input always produces the same output
  • This property allows password verification

3. Avalanche Effect:

  • Small changes in input produce completely different outputs
  • "password" and "password1" produce vastly different hashes

4. Fixed Output Length:

  • Regardless of input length, output is always the same size
  • SHA-256 always produces 256-bit (64 hex character) outputs

Common Hash Algorithms

AlgorithmOutput SizeStatusUse Case
MD5128 bits⚠️ DeprecatedNever use for passwords
SHA-1160 bits⚠️ DeprecatedNever use for passwords
SHA-256256 bits✅ AcceptableGeneral purpose, but not ideal for passwords
bcryptVariable✅ RecommendedSpecifically designed for passwords
Argon2Variable✅ RecommendedWinner of Password Hashing Competition
scryptVariable✅ RecommendedMemory-hard algorithm

⚠️ Important: MD5 and SHA-1 are cryptographically broken and should never be used for password storage. Use purpose-built password hashing algorithms like bcrypt or Argon2.


3. Limitations of Hashing Alone

Hashing alone is not sufficient to protect passwords. While it prevents direct password recovery, attackers have developed sophisticated methods to crack hashed passwords.

The Problem

If attackers obtain hashed passwords (e.g., through a database breach), they can still use:

1. Dictionary Attacks:

  • Attackers try common passwords against the stolen hashes
  • Tools like John the Ripper can test millions of passwords per second
  • Common passwords like "password123", "qwerty", "123456" are tested first

2. Rainbow Table Attacks:

  • Precomputed tables of password-hash pairs
  • Can instantly reveal passwords for millions of common passwords
  • Particularly effective against unsalted hashes

Real-World Example

Consider a database breach where attackers obtain this hash:

5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Without salting:

  1. Attacker checks rainbow table → instantly finds "password" matches
  2. Account compromised in seconds

With proper salting:

  1. Attacker can't use rainbow tables
  2. Must brute-force each password individually
  3. Computational cost increases dramatically

4. Rainbow Table Attacks

A rainbow table is a precomputed list of passwords and their corresponding hashes. These tables are massive databases that allow attackers to quickly reverse hash lookups.

How Rainbow Tables Work

Step 1: Precomputation Attackers generate hashes for millions of common passwords offline, creating lookup tables.

Example (SHA-1):

PasswordHash
1234567C4A8D09CA3762AF61E59520943DC26494F8941B
password5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
qwertyB1D37F37A9DBAB88EFF8B2A0A9E9B9E1E1E1E1E1
letmein0BD65E3F3D79E8D8A5C5C5C5C5C5C5C5C5C5C5C5

Step 2: Attack

  1. Attacker steals hashed passwords from a database
  2. Looks up each hash in the rainbow table
  3. If found, password is instantly revealed

Why Rainbow Tables Are Effective

  • Speed: Lookups are nearly instant (O(1) time complexity)
  • Coverage: Can include billions of common passwords
  • Efficiency: One table can crack millions of hashes simultaneously

Statistics:

  • Rainbow tables can contain up to several terabytes of data
  • Can crack 99% of common passwords in seconds
  • Most effective against unsalted hashes

Countermeasure: Salting

Rainbow tables become completely useless when passwords are properly salted, because each password requires a unique table based on its salt.


5. Salting Passwords (Critical Defense)

What Is a Salt?

A salt is a random, unique value added to a password before hashing. The salt is different for each password, even if the passwords are identical.

Hashing with Salt:

hash(password + salt)

Example:

Password: "mypassword"
Salt 1:   "a3f5b8c2"
Salt 2:   "x9k2m4n7"

Hash 1: hash("mypassword" + "a3f5b8c2") → "abc123..."
Hash 2: hash("mypassword" + "x9k2m4n7") → "xyz789..."

Same password, completely different hashes!

Why Salting Works

1. Uniqueness:

  • Two identical passwords produce different hashes
  • Even if multiple users have the same password, their hashes are different

2. Rainbow Table Invalidation:

  • Rainbow tables become useless because they don't account for salts
  • Attackers would need a separate table for each possible salt value
  • With 128-bit salts, that's 2^128 possible tables—computationally infeasible

3. Increased Attack Cost:

  • Attackers must recompute hashes for every password–salt combination
  • Can't precompute attacks; must attack each hash individually
  • Dramatically slows down brute-force attempts

Salt Storage

Critical Principle: Salts are usually stored alongside the hashed password in the database. This is secure because:

  • Salts don't need to be secret (they're often visible in database dumps)
  • The security comes from uniqueness, not secrecy
  • Without the salt, an attacker can't verify guesses

Database Schema Example:

| user_id | username | password_hash                    | salt      |
|---------|----------|----------------------------------|-----------|
| 1       | alice    | a3f5b8c2d1e4f6...               | x9k2m4n7  |
| 2       | bob      | b4e6c9d2e5f7a8...               | p8q3r5s9  |

6. Hash Iterations (Rounds)

Modern systems apply hashing multiple times (iterations or rounds) to increase computational cost.

How Iterations Work

Single Hash:

password → hash(password) → stored_hash

Multiple Iterations:

pass + salt → hash1
hash1 + salt → hash2
hash2 + salt → hash3
...
hash(n-1) + salt → hash(n) → stored_hash

Why Iterations Matter

Computational Cost:

  • Each iteration adds computational work
  • Makes brute-force attacks significantly slower
  • If each hash takes 100ms, 10,000 iterations = 1 second per attempt
  • Attacker trying 1 billion passwords now takes exponentially longer

The Trade-Off

Security vs. Performance:

  • More rounds = more security, but slower authentication
  • Need to balance user experience with security

Industry Standards:

  • bcrypt: Typically 10-12 rounds (cost factor 2^10 to 2^12)
  • Argon2: Configurable time, memory, and parallelism parameters
  • PBKDF2: Typically 100,000+ iterations

Adaptive Hashing

Modern systems use adaptive hashing—increasing iterations over time as hardware becomes faster. This maintains security levels without requiring password changes.


7. Password Storage in Linux Systems

Linux systems provide a robust example of secure password storage.

The /etc/shadow File

In Linux, password hashes are stored in /etc/shadow (not /etc/passwd for security reasons). This file is readable only by root.

Checking Your Linux System

View password hash format:

sudo cat /etc/shadow | grep username

Verify hashing algorithm:

# Check what algorithm is configured
authconfig --test | grep hashing

9. Defending Against Brute-Force Attacks

Understanding Brute-Force Attacks

A brute-force attack is an attempt to gain unauthorized access by systematically trying every possible password combination. Attackers use automated tools to repeatedly attempt logins until one succeeds.

Attack Methods:

  1. Simple Brute-Force: Try every possible character combination
  2. Dictionary Attack: Try common passwords and word lists
  3. Hybrid Attack: Combine dictionary words with variations (password123, Password!, etc.)

Attack Vectors

Common Targets:

  • SSH servers (port 22)
  • Web application login pages
  • FTP servers
  • Email services (IMAP/POP3)
  • Database authentication
  • API endpoints

Attack Tools:

  • Hydra: Network logon cracker
  • Medusa: Parallel login brute-forcer
  • John the Ripper: Password cracker
  • Aircrack-ng: Wi-Fi password cracking
  • Burp Suite: Web application testing

Why Brute-Force Attacks Work

Factors That Enable Attacks:

  • Weak passwords (common, short, predictable)
  • No rate limiting on login attempts
  • No account lockout policies
  • Predictable usernames (admin, root, test)
  • Exposed services on the internet

Statistics:

  • Average brute-force attack tries 1,000-10,000 passwords per hour
  • Sophisticated attacks can attempt millions of passwords
  • Most successful attacks target weak passwords (top 1,000 common passwords)

10. Fail2ban: Firewall-Level Protection

Fail2ban is an intrusion prevention system that monitors system logs and automatically blocks IPs that show malicious behavior.

How Fail2ban Works

1. Log Monitoring:

  • Reads logs from various services (SSH, FTP, Nginx, Apache, etc.)
  • Uses regex patterns to detect failed login attempts
  • Tracks patterns across time windows

2. Pattern Detection:

  • Identifies repeated failed authentication attempts
  • Tracks IP addresses and timestamps
  • Configurable thresholds (e.g., 5 failures in 10 minutes)

3. Automatic Action: When threshold is exceeded, Fail2ban triggers configured actions:

  • Block IP via firewall (iptables, firewalld, ufw, etc.)
  • Add to blacklist for permanent blocking
  • Send email alerts to administrators
  • Perform whois lookup to gather attacker information
  • Execute custom scripts

4. Automatic Unban:

  • After the configured ban time expires, IP is automatically unblocked
  • Prevents permanent lockout of legitimate users who made mistakes

Installation

Ubuntu/Debian:

sudo apt update
sudo apt install fail2ban

Configuration

Main Configuration File: /etc/fail2ban/jail.conf or /etc/fail2ban/jail.local

Key Parameters:

ParameterDescriptionExample
bantimeHow long an IP is blocked (seconds)3600 (1 hour)
findtimeTime window to count failures (seconds)600 (10 minutes)
maxretryFailed attempts before ban5
ignoreipWhitelisted IPs (never banned)127.0.0.1/8 ::1
actionWhat to do when banningaction_ options (see below)

Example Configuration:

[DEFAULT]
# Ban for 1 hour
bantime = 3600
 
# Look back 10 minutes
findtime = 600
 
# Ban after 5 failures
maxretry = 5
 
# Never ban localhost
ignoreip = 127.0.0.1/8 ::1
 
# Email alerts
destemail = admin@example.com
sender = fail2ban@example.com
 
[sshd]
enabled = true
port = ssh
logpath = /var/log/auth.log
maxretry = 3

Common Actions

Available Actions:

  • action_: Just log the ban
  • action_mw: Send email with whois information
  • action_mwl: Send email with whois and relevant log lines
  • action_iptables: Block using iptables (default)

Custom Actions: You can create custom actions for specific needs (webhooks, API calls, etc.)

Advantages

1. Firewall-Level Protection:

  • Operates at the firewall level, blocking before packets reach services
  • More efficient than application-level blocking

2. Multi-Service Support:

  • Can protect multiple services simultaneously
  • Centralized configuration and management

3. Highly Configurable:

  • Customizable regex patterns
  • Flexible action system
  • Service-specific configurations

4. Automatic Management:

  • Self-healing (auto-unban after timeout)
  • Minimal administrative overhead

Limitations

1. Log-Dependent:

  • Requires proper logging from services
  • Won't work if logs aren't accessible or formatted correctly

2. Reactive (Not Preventive):

  • Only responds after attacks begin
  • Doesn't prevent initial attempts

3. IP-Based Blocking:

  • Attackers can use proxy/VPN to change IPs
  • May block legitimate users sharing IPs (NAT, corporate networks)

11. DenyHost: Host-Based Protection

DenyHost is a Python script designed specifically for SSH protection. It monitors authentication logs and automatically blocks IPs that show malicious SSH login attempts.

How DenyHost Works

1. Log Monitoring:

  • Monitors /var/log/auth.log (Linux)
  • Parses SSH authentication failures
  • Tracks IP addresses and failure counts

2. Automatic Blocking: When threshold is exceeded:

  • Automatically adds offending IPs to /etc/hosts.deny
  • Uses TCP Wrappers for blocking (host-based, not firewall)
  • Prevents connections at the application layer

3. Suspicious Pattern Detection:

  • Identifies IPs attacking multiple usernames
  • Detects coordinated attacks
  • Can block entire IP ranges if needed

Installation

Ubuntu/Debian:

sudo apt update
sudo apt install denyhosts

Comparison: Fail2ban vs. DenyHost

FeatureFail2banDenyHost
Protection LevelFirewall-basedHost-based
Service SupportMany services (SSH, HTTP, FTP)SSH-focused
ConfigurationHighly configurableLightweight & simple
Blocking MethodFirewall rules (iptables)TCP Wrappers
Log SourcesMultiple log filesSSH auth logs only
ComplexityModerate to advancedSimple
Active DevelopmentYes (actively maintained)Limited
Best ForMulti-service protectionSSH-only environments

When to Use DenyHost

Choose DenyHost if:

  • You only need SSH protection
  • You prefer simplicity over flexibility
  • You're using TCP Wrappers already
  • You want lightweight, minimal configuration

Choose Fail2ban if:

  • You need protection for multiple services
  • You want firewall-level blocking
  • You need advanced configuration options
  • You want active community support

Recommendation: For most modern systems, Fail2ban is preferred due to its flexibility and active development. DenyHost is simpler but more limited.


12. TCP Wrappers: hosts.allow & hosts.deny

TCP Wrappers provide host-based access control for services that support them. While older technology, understanding TCP Wrappers is important for system administration.

How TCP Wrappers Work

Linux systems can control access using two files:

  • /etc/hosts.allow: Specifies allowed hosts/services
  • /etc/hosts.deny: Specifies denied hosts/services

Supported Services: Services compiled with libwrap support TCP Wrappers:

  • SSH (if compiled with TCP Wrapper support)
  • FTP
  • Telnet
  • Some email services
  • Various network services

Rule Precedence

Evaluation Order:

  1. hosts.allow is checked first
  2. If no match, hosts.deny is checked
  3. If no match in either file → access allowed by default

Important: First match wins! If a rule in hosts.allow matches, access is granted regardless of hosts.deny.

Limitations

1. Not Universal:

  • Many modern services don't support TCP Wrappers
  • Systemd services often bypass TCP Wrappers

2. Limited Granularity:

  • Can't block by port (blocks entire service)
  • Less flexible than firewall rules

3. Deprecated:

  • Considered legacy technology
  • Modern systems prefer firewall-based solutions

Modern Alternative

Use firewall tools instead:

  • iptables/ufw: Linux firewall
  • firewalld: Red Hat firewall
  • nftables: Modern Linux firewall
  • Cloud provider security groups: AWS, Azure, GCP

Comprehensive Defense Strategy

Layered Security Approach

Strong authentication requires multiple layers of defense working together:

Layer 1: Password Security

  • ✅ Strong password policies (length, complexity)
  • ✅ Secure hashing (bcrypt, Argon2) with salt
  • ✅ Multiple iterations/rounds
  • ✅ Never store plaintext passwords

Layer 2: Attack Prevention

  • ✅ Fail2ban or DenyHost for brute-force protection
  • ✅ Rate limiting on login attempts
  • ✅ Account lockout policies
  • ✅ Firewall rules restricting access

Layer 3: Authentication Enhancement

  • ✅ Single Sign-On (SSO) for centralized management
  • ✅ Certificate-based authentication (for SSH)

Layer 4: Monitoring and Alerting

  • ✅ Log all authentication attempts
  • ✅ Monitor for suspicious patterns
  • ✅ Alert on repeated failures
  • ✅ Regular security audits

Security Checklist

Password Storage:

  • Using bcrypt, Argon2, or scrypt (never MD5/SHA-1)
  • Unique salt for each password
  • Sufficient iterations/rounds (10+ for bcrypt)
  • Passwords never stored in plaintext or logs

Attack Protection:

  • Fail2ban or DenyHost configured and running
  • Rate limiting implemented
  • Account lockout after failed attempts
  • Firewall rules restricting SSH access

Authentication:

  • Strong password policies enforced
  • Regular password rotation (if required)
  • SSO implemented (for enterprise)

Monitoring:

  • Authentication logs reviewed regularly
  • Failed login attempts monitored
  • Alerts configured for suspicious activity
  • Security incidents documented

Conclusion

User authentication is not a single feature—it is a system of protections working together. In today's threat landscape, a single password is never enough.

Key Takeaways:

  1. Never Store Plaintext Passwords

    • Use secure hashing algorithms (bcrypt, Argon2)
    • Always salt passwords uniquely
    • Implement multiple iterations for computational cost
  2. Protect Against Brute-Force Attacks

    • Use Fail2ban or DenyHost for automatic IP blocking
    • Implement rate limiting and account lockouts
    • Restrict access with firewalls and TCP Wrappers
  3. Enhance Authentication

    • Use strong password policies
    • Consider certificate-based authentication
  4. Monitor and Respond

    • Log all authentication attempts
    • Monitor for suspicious patterns
    • Have an incident response plan

Remember: Security is not a one-time setup. It requires ongoing monitoring, updates, and adaptation to new threats. As attackers develop new techniques, your defenses must evolve.

By implementing these practices, you create a robust authentication system that protects users, data, and infrastructure from the ever-evolving threat landscape. Start with the fundamentals—proper password hashing and salting—then layer on additional protections based on your specific needs and threat model.

💬 Your Action Items:

  1. Audit your current password storage methods
  2. Implement Fail2ban or DenyHost if you haven't already
  3. Review and update your security policies regularly

The security of your systems depends on getting these fundamentals right. Start today.