I'm upgrading from Spring Security 4.x to 5.x.
The ReflectionSaltSource
from Spring 4 lets us configure a custom salt. But that's removed in Spring Security 5. I then found out that I should use MessageDigestPasswordEncoder
. It has a long detailed java-doc but unfortunately the doc is a bag of words without conveying any structured information (I tried multiple times; my bad if I was ignorant).
Anyways I thought I should do the following based on my limited understanding.
Old system with 4.x - myEncodedPassword
and mySalt
are passed separately to the encoder.
New System with 5.x - Pass one field with the value {mySalt}myEncodedPassword
to the MessageDigestPasswordEncoder
However, that did not work. The Problem was that when MessageDigestPasswordEncoder
sees {mySalt}encodedPassword
, it uses {mySalt}
(with the {}) as the salt instead of using mySalt
as the salt . I'm confused.
Here's a coding example. I used Groovy to reduce noise.
@Grab(group='org.springframework.security', module='spring-security-core', version='5.1.4.RELEASE')
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder
String password = 'myPassword'
String salt_1 = 'mySalt'
String salt_2 = '{mySalt}'
// http://www.lorem-ipsum.co.uk/hasher.php generated below hashes
String encodedPasswordWithSalt_1 = '57bc828628811a10496215e217b7ae9b714c859fc7a8b1c678c9a0cc40aac422'
String encodedPasswordWithSalt_2 = 'a18b53fc58843def1e08e00a718f40d6f8eda0b97ef97824b5078c1fad93c0c5'
MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder('SHA-256')
println "expected=true, actual=" + encoder.matches(password, "{${salt_1}}${encodedPasswordWithSalt_1}") // <--- expected to match but did not
println "expected=false, actual=" + encoder.matches(password, "{${salt_1}}${encodedPasswordWithSalt_2}") // <--- why does this match?
The output is
expected=true, actual=false
expected=false, actual=true
I'm hoping to find a way to support SHA256 with custom and separate salt for each user password.
If anyone's interested, I created a ticket on GitHub - https://github.com/spring-projects/spring-security/issues/6594 . No solution so far. I will update here if there's any. So this is still an unanswered question.
I guess the issue is in the org.springframework.security.crypto.password.MessageDigestPasswordEncoder
class
By debugging it in the method private String extractSalt(String prefixEncodedPassword)
they try to extract salt by returning prefixEncodedPassword.substring(start, end + 1);
where start
is the index of the prefix {
while end is the index of suffix }
and it stops to the first suffix it matches so what happens in your code?
It happens this:
MessageDigestPasswordEncoder encoder = new MessageDigestPasswordEncoder('SHA-256')
println "expected=true, actual=" + encoder.matches(password, "{${salt_1}}${encodedPasswordWithSalt_1}") //It's not matched because the extracted salt will be {mySalt} and not mySalt
println "expected=false, actual=" + encoder.matches(password, "{${salt_1}}${encodedPasswordWithSalt_2}") //It's matched because the extracted salt will be {mySalt} and not mySalt
I state I don't know if it's a bug or not, in any case in your scenario it should be enough to properly investigate and properly modify the method
private String extractSalt(String prefixEncodedPassword) {
int start = prefixEncodedPassword.indexOf(PREFIX);
if (start != 0) {
return "";
}
int end = prefixEncodedPassword.indexOf(SUFFIX, start);
if (end < 0) {
return "";
}
return prefixEncodedPassword.substring(start, end + 1);
}
This method is inside the org.springframework.security.crypto.password.MessageDigestPasswordEncoder
class
I hope it's useful
Angelo
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.