Fetching latest headlines…
Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples)
NORTH AMERICA
🇺🇸 United StatesApril 19, 2026

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples)

0 views0 likes0 comments
Originally published byDev.to

What if I told you that even if you hash passwords, an attacker might still crack them in seconds?

Authentication is one of the most critical parts of any application—and also one of the most misunderstood.

In this post, we’ll think like an attacker, break insecure implementations using Java examples, and then progressively strengthen our defenses using hashing and salting.
If you’ve ever stored a password using only hashing, your system may still be vulnerable.

🧠 1. Why Authentication Security Matters

When authentication fails, everything fails:

  • Account takeover
  • Data breaches
  • Privilege escalation

To build secure systems, we must first understand how attackers operate.

⚔️ 2. How Attackers Break Authentication

(i). Brute Force Attack

Attacker tries all possible passwords until one works.
👉 Works because:

  • Users choose weak passwords
  • Systems don’t limit attempts

(ii). Dictionary Attack

Instead of all combinations, attacker uses a list of common passwords:
123456
password
admin
welcome123

(iii). Rainbow Table Attack

Attacker precomputes hashes:
password → hash1
admin → hash2

Then instantly matches stolen hashes.
👉 Extremely fast if no salt is used

(iv). Session Attacks (brief)

Focus on hijacking authenticated sessions (cookies, tokens).
👉 Important, but outside this blog’s main focus.

🔴 3. Thinking Like an Attacker: Breaking Weak Authentication

3.1 Brute Force Simulation (Java)

package com.auth;

public class BruteForceDemo {

    public static void main(String[] args) {

        String actualPassword = "1234";

        for (int i = 0; i <= 9999; i++) {
            String guess = String.format("%04d", i);

            System.out.println("Trying: " + guess);

            if (guess.equals(actualPassword)) {
                System.out.println("==> Password found: " + guess);
                break;
            }
        }
    }
}

👉 This works because:

  • Password is short and predictable
  • No rate limiting

3.2 Hashing Alone is NOT Enough

Let’s say system stores:
hash(password)
Java Example

package com.auth;

import java.security.MessageDigest;

public class HashAttackDemo {

    public static String hash(String input) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest(input.getBytes());

        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    public static void main(String[] args) throws Exception {

        String storedHash = hash("password");

        String[] guesses = {"123456", "password", "admin"};

        for (String guess : guesses) {
            if (hash(guess).equals(storedHash)) {
                System.out.println("==> Cracked: " + guess);
            }
        }
    }
}

👉 Even though password is hashed, attacker can still:

  • Hash guesses
  • Compare results

3.3 Rainbow Table Attack (Precomputation)

package com.auth;

import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;

public class RainbowTableDemo {

    public static String hash(String input) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(input.getBytes());

        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    public static void main(String[] args) throws Exception {

        String[] passwords = {"123456", "password", "admin"};

        Map<String, String> table = new HashMap<>();

        for (String pwd : passwords) {
            table.put(hash(pwd), pwd);
        }

        String stolenHash = hash("password");

        if (table.containsKey(stolenHash)) {
            System.out.println("==> Instantly cracked: " + table.get(stolenHash));
        }
    }
}

👉 No guessing needed — just lookup.

🛡️ 4. Why SALT Changes Everything

4.1 Why SALT is Needed


Problem:
password → same hash everywhere
Solution:
hash(password + salt)
👉 Makes each hash unique
4.2 SALT Implementation (Java)

package com.auth;

import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;

public class SaltedHashDemo {

    public static String generateSalt() {
        byte[] salt = new byte[16];
        new SecureRandom().nextBytes(salt);
        return Base64.getEncoder().encodeToString(salt);
    }

    public static String hash(String password, String salt) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest((password + salt).getBytes());

        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    public static void main(String[] args) throws Exception {

        String password = "password";
        String salt = generateSalt();

        String hashed = hash(password, salt);

        System.out.println("Salt: " + salt);
        System.out.println("Hash: " + hashed);
    }
}

4.3 How SALT Breaks Rainbow Attacks

password + salt1 → hash1
password + salt2 → hash2

👉 Same password ≠ same hash
👉 Rainbow tables become useless

🔴 5. Attacker vs SALT

5.1 Attacking Salted Hashes (Java)

package com.auth;

import java.security.MessageDigest;

public class SaltedAttackDemo {

    public static String hash(String password, String salt) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] digest = md.digest((password + salt).getBytes());

        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    public static void main(String[] args) throws Exception {

        String salt = "randomSalt123";
        String storedHash = hash("password", salt);

        String[] dictionary = {"123456", "password", "admin"};

        for (String guess : dictionary) {
            if (hash(guess, salt).equals(storedHash)) {
                System.out.println("==> Cracked even with salt: " + guess);
            }
        }
    }
}

👉 Salt does NOT stop guessing — only slows it down.

5.2 Cost Explosion

Without salt:
1M guesses → cracks many users
With salt:
1M users × 1M guesses = 1 trillion operations

Source Code

All source files are available on GitHub:
Github source codes

👉 This is where salt becomes powerful.

⚠️ 6. What SALT Does NOT Solve

  • Weak passwords (still crackable)
  • Fast hashing (SHA-256 is too fast)
  • GPU-based attacks

👉 SALT makes attacks harder—but not impossible.
Attackers can still:

  • Perform brute force attacks
  • Use GPUs to compute hashes at scale
  • Target weak passwords

This is why modern systems go beyond

🚀 Final Defense: Modern Password Hashing
Use:

Why:

  • Built-in salt
  • Slow hashing (costly per attempt)
  • Resistant to GPU attacks

🧠 7.Conclusion

Authentication security evolves like this:
Plain text → completely broken
Hash only → still vulnerable
Salted hash → better
Salt + slow hashing → strong defense

👉 Security is not about making attacks impossible,
👉 It’s about making them economically infeasible.

💡 8.Final Thought

Think like an attacker:

  • Can I guess this password?
  • Can I reuse work?
  • Can I scale this attack?

Good security doesn’t make attacks impossible—it makes them impractical.

As a developer, your goal isn’t to stop attackers completely, but to ensure that breaking your system is simply not worth the effort.

Comments (0)

Sign in to join the discussion

Be the first to comment!