From c1aa99fdd2113a232986e9c3a44673ae752de840 Mon Sep 17 00:00:00 2001 From: Joe Grandja <10884212+jgrandja@users.noreply.github.com> Date: Thu, 17 Apr 2025 04:53:33 -0400 Subject: [PATCH] Enforce BCrypt password length for new passwords only Closes gh-16802 --- .../security/crypto/bcrypt/BCrypt.java | 3 +- .../bcrypt/BCryptPasswordEncoderTests.java | 31 ++++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java b/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java index 172928401e..4f19f52de0 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java +++ b/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java @@ -611,7 +611,8 @@ public class BCrypt { int rounds, off; StringBuilder rs = new StringBuilder(); - if (passwordb.length > 72) { + // Enforce max length for new passwords only + if (!for_check && passwordb.length > 72) { throw new IllegalArgumentException("password cannot be more than 72 bytes"); } if (salt == null) { diff --git a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java index df14ebe906..f2921064fa 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java @@ -223,13 +223,34 @@ public class BCryptPasswordEncoderTests { } @Test - public void enforcePasswordLength() { + public void encodeWhenPasswordOverMaxLengthThenThrowIllegalArgumentException() { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + String password72chars = "123456789012345678901234567890123456789012345678901234567890123456789012"; - assertThat(encoder.matches(password72chars, encoder.encode(password72chars))).isTrue(); - String password73chars = password72chars.concat("a"); - assertThatIllegalArgumentException() - .isThrownBy(() -> encoder.matches(password73chars, encoder.encode(password73chars))); + encoder.encode(password72chars); + + String password73chars = password72chars + "3"; + assertThatIllegalArgumentException().isThrownBy(() -> encoder.encode(password73chars)); + } + + @Test + public void matchesWhenPasswordOverMaxLengthThenAllowToMatch() { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + + String password71chars = "12345678901234567890123456789012345678901234567890123456789012345678901"; + String encodedPassword71chars = "$2a$10$jx3x2FaF.iX5QZ9i3O424Os2Ou5P5JrnedmWYHuDyX8JKA4Unp4xq"; + assertThat(encoder.matches(password71chars, encodedPassword71chars)).isTrue(); + + String password72chars = password71chars + "2"; + String encodedPassword72chars = "$2a$10$oXYO6/UvbsH5rQEraBkl6uheccBqdB3n.RaWbrimog9hS2GX4lo/O"; + assertThat(encoder.matches(password72chars, encodedPassword72chars)).isTrue(); + + // Max length is 72 bytes, however, we need to ensure backwards compatibility + // for previously encoded passwords that are greater than 72 bytes and allow the + // match to be performed. + String password73chars = password72chars + "3"; + String encodedPassword73chars = "$2a$10$1l9.kvQTsqNLiCYFqmKtQOHkp.BrgIrwsnTzWo9jdbQRbuBYQ/AVK"; + assertThat(encoder.matches(password73chars, encodedPassword73chars)).isTrue(); } }