Procházet zdrojové kódy

SEC-1890: Add checks for validity of stored bcrypt hash

When checking for a match, the BCryptPasswordEncoder validates
the stored hash against a pattern to check that it actually is
a bcrypt value.
Luke Taylor před 13 roky
rodič
revize
3760d792ea

+ 10 - 1
crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.java

@@ -16,6 +16,7 @@
 package org.springframework.security.crypto.bcrypt;
 package org.springframework.security.crypto.bcrypt;
 
 
 import java.security.SecureRandom;
 import java.security.SecureRandom;
+import java.util.regex.Pattern;
 
 
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 
 
@@ -28,6 +29,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
  *
  *
  */
  */
 public class BCryptPasswordEncoder implements PasswordEncoder {
 public class BCryptPasswordEncoder implements PasswordEncoder {
+    private Pattern BCRYPT_PATTERN = Pattern.compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}");
 
 
     private final int strength;
     private final int strength;
 
 
@@ -71,7 +73,14 @@ public class BCryptPasswordEncoder implements PasswordEncoder {
     }
     }
 
 
     public boolean matches(CharSequence rawPassword, String encodedPassword) {
     public boolean matches(CharSequence rawPassword, String encodedPassword) {
+        if (encodedPassword == null || encodedPassword.length() == 0) {
+            throw new IllegalArgumentException("Encoded password cannot be null or empty");
+        }
+
+        if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) {
+            throw new IllegalArgumentException("Encoded password does not look like BCrypt");
+        }
+
         return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
         return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
     }
     }
-
 }
 }

+ 38 - 4
crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoderTests.java

@@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue;
 
 
 import org.junit.Test;
 import org.junit.Test;
 
 
+import java.security.SecureRandom;
+
 
 
 /**
 /**
  * @author Dave Syer
  * @author Dave Syer
@@ -44,17 +46,49 @@ public class BCryptPasswordEncoderTests {
     }
     }
 
 
     @Test
     @Test
-    public void matchesLengthChecked() {
+    public void notMatches() {
         BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
         BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
         String result = encoder.encode("password");
         String result = encoder.encode("password");
-        assertFalse(encoder.matches("password", result.substring(0,result.length()-2)));
+        assertFalse(encoder.matches("bogus", result));
     }
     }
 
 
     @Test
     @Test
-    public void notMatches() {
+    public void customStrength() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(8);
+        String result = encoder.encode("password");
+        assertTrue(encoder.matches("password", result));
+    }
+
+    @Test
+    public void customRandom() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(8, new SecureRandom());
+        String result = encoder.encode("password");
+        assertTrue(encoder.matches("password", result));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void barfsOnNullEncodedValue() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        assertFalse(encoder.matches("password", null));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void barfsOnEmptyEncodedValue() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        assertFalse(encoder.matches("password", ""));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void barfsOnShortEncodedValue() {
         BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
         BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
         String result = encoder.encode("password");
         String result = encoder.encode("password");
-        assertFalse(encoder.matches("bogus", result));
+        assertFalse(encoder.matches("password", result.substring(0, 4)));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void barfsOnBogusEncodedValue() {
+        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+        assertFalse(encoder.matches("password", "012345678901234567890123456789"));
     }
     }
 
 
 }
 }