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.
Top comments (0)