inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

스프링 시큐리티

스프링 시큐리티 커스터마이징: PasswordEncoder

BCrypt 인증 관련 질문드립니다.

1596

YongIl Park

작성한 질문수 7

1

안녕하세요.

spring security 권장사항이 BCrypt라고 해서 조금 찾아보고

테스트를 해봤는데요.

<첫 번째 시도>

password = !@#$password1234

passwordHashed = $2a$10$foL9uBBw3knu9QoKmVb64.pRTRsxy96NQUQanjhOzl8D1yEoLs73m

isValidPassword = true

<두 번째 시도>

password = !@#$password1234

passwordHashed = $2a$10$abpfdjC6qWnj687evfjzx.bV0Xlkas7wcx0s8OT2UwQD1Huo54oyi

isValidPassword = true

보면 솔트를 랜덤으로 생성하기 때문에

인코딩된 패쓰워드가 다르게 나오는데

어떻게 인증이 되는건가요..?

어.. 그러니까 만약 Bcyrpt 솔트 기본값 10으로

회원등록을 하고 (첫 번째 시도)

이 걸로 다시 로그인을 하면 두 번째 시도의 값이 

나와서 두 개가 다르다고 인식을 할 거 같은데 

어떻게 옳은 패스워드로 스프링 시큐리티가 판단하는지 

궁금합니다.

아니면 솔트를 사용하는게 아닌건지..궁금합니다.

java spring

답변 9

5

백기선

시간이 오래 지났지만 요즘 새 강좌를 만들다가 문득 이 질문이 다시 생각나서 찾아봤습니다. 우선 사과드립니다. 제가 잘못 알고 있었네요. 수강평에 남겨주신대로 테스트를 작성하지 않고 댓글을 남겨 죄송합니다.

결론부터 말씀드리자면, 기본 생성자를 사용한 BCryptPasswordEncode로 encoding을 할 때 salt는 매번 바뀌는게 맞습니다. 대신 matches로 plain 패스워드와 인코딩 된 패스워드를 비교할 때는 salt를 사용하지 않습니다. 오로지 plain 패스워드와 인코딩된 패스워드만 가지고 해시를 하고 그 결과는 인코딩 된 패스워드와 일치하게 되어있습니다. 따라서 salt가 바뀌는건 신경쓰지 않아도 됩니다. 인코딩 할 때만 쓰이니까요.

덕분에 해시 기반 패스워드 동작 원리를 다시 한번 돌아보는 시간이 되었네요. 감사합니다.

2

쿠크다스

저도 궁금해서 찾아봤는데 아래 참고하면 좋을 것 같아요!

참고

https://stackoverflow.com/questions/36422180/retrieving-password-salt-with-bcryptpasswordencoder-in-spring

https://stackoverflow.com/questions/6832445/how-can-bcrypt-have-built-in-salts

https://d2.naver.com/helloworld/318732

0

YongIl Park

너무 늦게 봤네요.

자세한 정보 감사합니다.

새해 복 많이 받으시길 바라겠습니다.

0

YongIl Park

프로그램 재가동시 바뀝니다. (위 코드 테스트 결과도 fail되는군요..)

프로그램을 처음 초기화 할 경우 생성자에서 솔트를 랜덤 생성해서 넣어주기 때문이죠.

위에 이것에 대해서 다 글을 써놓았는데요.

제가 그래서 의문점이 생긴것이 그러면 어떻게 인코더는 프로그램을 재가동했을때

솔트를 알 수 있나 ? 이것 이었습니다.

왜냐하면 그 부분을 정확히 이해가 안된다면 프로그램 개발을 하고도 찝찝할 수 박에 없기 때문이죠.

-----------------------------------------------------------------------------------------------

테스트 결과 원래인코딩된 패스워드를 솔트로 제공하면 어떻게 알아내는것인지는 모르겠지만

그 전에 어떤 솔트 값을 넣었는지 몰라도 같은 인코딩 결과가 나오게 됩니다.

물론 이 내용도 위 글에서 다 써놓았습니다..

BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String firstEncoded = passwordEncoder.encode("password");
String secondEncoded = passwordEncoder.encode("password");

System.out.println(firstEncoded);
System.out.println(secondEncoded);

<첫 번째 가동시>
$2a$10$bGPFYVPMIodBr3kPBNiA4u8MmOoqeDaTTqwSvwks57Qyci4fjNltK $2a$10$1OxvWR6cYdJYO6/QSFHHDeevmbuaCirH4ZJdezwKzihHuVnlwMynG

<두 번째 가동시>
$2a$10$h0GZ96jg5BR2QDQxWtxQKuehwx.VLOw2QYFQX0PXmZhT94Eh6kPki $2a$10$m.e0/kCIsuY3NODsVVw8xu0JZrprfY3Y2YhFduCEUOWLlpJFHUq1a

BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String firstEncoded = BCrypt.hashpw("password", "$2a$10$bGPFYVPMIodBr3kPBNiA4u8MmOoqeDaTTqwSvwks57Qyci4fjNltK");

System.out.println(firstEncoded);

<첫 번째 가동시 인코딩된 값을 BCrypt.hashpw의 솔트 전달 부분에 전달했을 경우 인코딩된 값>
$2a$10$bGPFYVPMIodBr3kPBNiA4u8MmOoqeDaTTqwSvwks57Qyci4fjNltK

0

백기선

@Test
public void bcryptPassword() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String firstEncoded = passwordEncoder.encode("password");
String secondEncoded = passwordEncoder.encode("password");
assertEquals(firstEncoded, secondEncoded);
}


BCrypt 패스워드 인코더의 솔트값은 바뀌질 않습니다. 

0

YongIl Park

허허 백선장님 ..  디버깅을 하며 분석하다 

테스트를 해봤더니요.

String password = "!@#$password1234";
String passwordHashed = BCrypt.hashpw(password, BCrypt.gensalt());

String hashpw = BCrypt.hashpw(password, passwordHashed);
System.out.println("first hashed = " + passwordHashed);
System.out.println("second hashed = " + hashpw);

first hashed = $2a$10$csCCTJlus2OXEyAHmYPmNOCtkEj8lv61WnJRVkVELo0GKjc4bIif. second hashed = $2a$10$csCCTJlus2OXEyAHmYPmNOCtkEj8lv61WnJRVkVELo0GKjc4bIif.

이렇게 나오네요.
즉 첫 번째 인자에 rawpassword, 두 번째 인자에 인코딩된 비밀번호
를 넣으면

첫 번째 인장에 rawpassword, 두 번째 인자에
BCrypt.gensalt()넣었던 결과가 똑같이
나오게 되는군요.


0

YongIl Park

음 계정을 만드는 부분

this.password = passwordEncoder.encode(this.password);
을 디버그 해보니
PasswordEncoderFactories 에서 bCrypt를 생성할때  SecureRandom random 인자에 null을 주면

BCryptPasswordEncoder
public String encode(CharSequence rawPassword) {
String salt;
if (strength > 0) {
if (random != null) {
salt = BCrypt.gensalt(strength, random);
}
else {
salt = BCrypt.gensalt(strength);
}
}
else {
salt = BCrypt.gensalt();
}

에서 보이듯이 gensalt를 실행해서 salt를 얻는데요.
이 부분을 반복적으로 실행해보니 항상 다른 salt가 나오던데..
그렇다면 고정된 값이 아니지 않나요..?

0

YongIl Park

저.. 그런데 제가 궁금해서 더 테스트를 해봤는데요.

String passwordHashed = BCrypt.hashpw(password, null);

이렇게 테스트를 했더니 salt값으로 null을 넣을 수 없다고 하는데
스프링 시큐리티
PasswordEncoderFactories에서는 생성자에서 null로 입력해서 하는데
그 이후에 실제로 어떻게 hashing을 하는지 그 과정이 궁금합니다.

0

YongIl Park

PasswordEncoderFactories에서

public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());

이렇게 하고

public BCryptPasswordEncoder() {
this(-1);
}

이렇게

public BCryptPasswordEncoder(int strength) {
this(strength, null);
}

이렇게 되어있네요..

0

백기선

https://docs.spring.io/spring-security/site/docs/4.2.12.RELEASE/apidocs/org/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.html

JavaDoc을 보시면 생성자에서 강도와 솔트를 생성자로 받는걸 볼 수 있습니다. 즉, 솔트와 강도는 고정값이지 매번 바뀌는 값이 아니니까 만드신 예제처럼 바뀌는 경우는 발생하지 않을 겁니다.

커스텀 로그인 페이지를 등록할 경우 LogoutFilter의 등록 여부 질문

0

383

1

anonymousClass를 사용하는 이유

0

387

1

ExcpetionTranslationFilter가 FilterSecurityInterceptor에서 발생하는 예외만 처리하는 이유

0

242

1

Principal 인터페이스와 User 클래스의 관계

0

756

1

passwordEncoder 질문드립니다.

0

320

1

WebSecurityConfigurerAdapter is deprecated 가 불편하신 분들을 위해

6

864

1

왜 스프링은 userid가 아니고 username을 사용했을까요?

0

1021

1

무상태성

0

346

2

로그인 후 로그인 페이지 접속

0

282

1

AuthenticationException은 어떤 경우에 발생하는지

0

556

1

AccountService에 비밀번호

1

224

1

커스텀 어노테이션 의 `커스텀 value` 설정방법에 관한 질문입니다.

0

356

1

passwordEncoder 빈 생성 시 차이

0

252

1

Bean 등록 관련 질문입니다.

0

247

1

시큐리티 전략관련

0

354

3

브라우저 기반 요청이 클라이언트의 요청을 처리 -> 의 의미?

0

228

1

AccountControllerTest 실행오류

0

1066

1

알려주신대로 설정을 해도 다시 302요청을 보냅니다....

0

342

1

voter에 대해 질문이 있습니다.

0

225

1

제가 맞게 이해한것인지 궁금합니다.

1

233

1

Spring security test 수행시 @AuthenticationPrincipal 가 null 로 세팅됨

0

546

1

Spring security Multi 인증 문의드려요

0

287

1

ProviderManager 문의

0

267

1

UserDetailsService 가 DaoAuthenticationProvider 에 어떻게 주입 되는지 알 수 있을 까요?

0

326

1