Careeroza
Password Policies
Modern Rules (NIST SP 800-63B)
- Length beats complexity: require at least 8 characters (ShopEase uses 12), allow at least 64, allow spaces and emoji — passphrases are good.
- Block known-bad passwords: common lists and breached passwords, plus ones containing the username or app name.
- No forced composition rules ("one uppercase, one symbol") and no forced periodic changes; they lead to
Password@1,Password@2... - Force a change only when there is evidence of compromise.
A Policy Checker
Example
import java.util.*;
public class Main {
static final Set<String> BLOCKLIST = Set.of("password123", "qwerty123456", "123456789012", "iloveyou1234");
static List<String> check(String password, String email) {
List<String> problems = new ArrayList<>();
String lower = password.toLowerCase();
String userPart = email.substring(0, email.indexOf('@')).toLowerCase();
if (password.length() < 12) problems.add("at least 12 characters");
if (password.length() > 128) problems.add("at most 128 characters");
if (BLOCKLIST.contains(lower)) problems.add("too common");
if (lower.contains("shopease")) problems.add("must not contain the app name");
if (userPart.length() >= 3 && lower.contains(userPart)) problems.add("must not contain your email name");
if (password.matches(".*(.)\\1\\1.*")) problems.add("no character 3 times in a row");
return problems;
}
public static void main(String[] args) {
String email = "[email protected]";
List<String> candidates = List.of(
"Asha@123",
"password123",
"ShopEase@2026!",
"asha.verma2026!",
"Paaaassword-long",
"monsoon chai at 5 pm",
"Tr7#kLp9!qZx");
for (String pw : candidates) {
List<String> problems = check(pw, email);
System.out.printf("%-22s %s%n", pw, problems.isEmpty() ? "OK" : "REJECTED: " + String.join(", ", problems));
}
}
}Output
Asha@123 REJECTED: at least 12 characters password123 REJECTED: at least 12 characters, too common ShopEase@2026! REJECTED: must not contain the app name asha.verma2026! REJECTED: must not contain your email name Paaaassword-long REJECTED: no character 3 times in a row monsoon chai at 5 pm OK Tr7#kLp9!qZx OK
Notice monsoon chai at 5 pm passes: a long, memorable passphrase is as strong as the random Tr7#kLp9!qZx (also OK, exactly 12 characters) and far easier to remember. password123 breaks two rules at once.
Breached Passwords and Upgrading Hashes
// Spring Security 6.3+: checks the password against Have I Been Pwned (k-anonymity: only
// the first 5 characters of the SHA-1 hash leave your server)
@Bean
CompromisedPasswordChecker compromisedPasswordChecker() {
return new HaveIBeenPwnedRestApiPasswordChecker();
}
@PostMapping("/api/auth/signup")
public ResponseEntity<?> signup(@Valid @RequestBody SignupRequest req) {
List<String> problems = passwordPolicy.check(req.password(), req.email());
if (compromisedPasswordChecker.check(req.password()).isCompromised()) {
problems.add("this password appeared in a data breach");
}
if (!problems.isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("password", problems));
}
userService.register(req.email(), passwordEncoder.encode(req.password()));
return ResponseEntity.status(HttpStatus.CREATED).build();
}Tip: Use PasswordEncoderFactories.createDelegatingPasswordEncoder(). Hashes are stored with a prefix like {bcrypt}$2a$10$..., so you can move to Argon2 later; implement UserDetailsPasswordService and Spring re-hashes each user's password automatically at their next login.