Spring/Spring Security

[Spring Security] 3-3. Authentication: Password Storage

noahkim_ 2023. 10. 2. 19:01

1. PasswordEncoder

  • 비밀번호 암호화를 담당하는 객체

 

one-way hashing

  • 한 번 해싱하면 원래 입력값으로 되돌릴 수 없음 (복호화 불가)
항목 설명
목적
비밀번호를 안전하게 저장하기 위함
인증 방식
입력된 비밀번호를 해싱하여, DB에 저장된 해시값과 비교
장점
원문 비밀번호를 저장하지 않으므로 데이터 유출 시 위험 최소화

 

2. Password Storage History

구분 설명 문제점 / 대응
초기 보안 처리 비밀번호를 평문으로 저장
DB 접근 제어 기능에 의존
계정 정보 탈취 위험
- SQL Injection: 입력값 공격
단방향 해싱 도입 단방향 해시 함수 사용 (해시값은 복호화 불가능)
→ 입력 비밀번호를 해시하여 저장된 값과 비교
사전 공격 가능성 존재
- 레인보우 테이블: 다양한 해시값을 미리 계산해둔 테이블
Salt 도입 Salt를 해싱에 함께 사용
→ 각 해시값을 고유하게 만듦
Salt 추가로 레인보우 테이블 무력화
Adaptive One-Way Functions 도입 하드웨어 성능 발전으로 인해 salt별 레인보우 테이블 생성 가능
→ work-factor 조정 (해시 생성 과정에서 필요한 작업량)
계산 비용 증가로 보안성 확보
단, 성능 저하 trade-off 고려 필요
단기 자격 증명으로 전환 세션 ID / 토큰 발급 (제한 시간 내에만 유효)
 노출 가능성을 낮추고 보안 매커니즘 성능을 아낌
매 요청마다 패스워드 검증 필요 없음
→ 성능 향상 + 보안 강화

 

3. DelegatingPasswordEncoder

구분 내용
기존 문제점
- Spring Security 5.0 이전 기본값은 NoOpPasswordEncoder
- 비밀번호를 평문으로 저장하여 보안에 매우 취약
현실적 제약
- 이미 NoOpPasswordEncoder를 사용하는 레거시 시스템이 많음
- 해시 기반 마이그레이션은 비용과 복잡성 존재
보안 정책의 변화
- 비밀번호 저장 방식은 시대에 따라 계속 진화
- Spring Security는 자주 기본값을 변경하기 어려움
→ DelegatingPasswordEncoder 도입
- 현재 권장되는 방식으로 비밀번호 저장
- 이전 포맷과 호환되어 검증 가능
- 미래 인코딩 방식으로의 유연한 전환 가능

 

Password Storage Format

{id}encodedPassword

  • id : 원본 패스워드를 해싱한 PasswordEncoder
  • encodedPassword : PasswordEncoder가 해싱한 값

 

 

예제

더보기
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0

 

Password Encoding

  • password storage format의 {id} 값은 DelegatingPasswordEncoder 생성자의 idForEncode 값으로 전달됨
  • idForEncode 값을 통해 PasswordEncoder의 구현체가 결정됩니다.
  • PasswordEncoder의 구현체는 passwordEncoderForEncode 필드로 담겨지며 encoding 작업을 위임받게 됩니다.

 

Password Matching

DelegatingPasswordEncoder.match(): 입력된 패스워드와 해싱 패스워드의 일치를 검사

  • DelegatingPasswordEncoder가 해싱 패스워드의 {id} 값에 알맞는 위임할 PasswordEncoder 구현체를 찾습니다.
  • 위임할 PasswordEncoder가 없거나, 매핑되어 있지 않다면 IllegalArgumentException를 던집니다.
  • 위임할 PasswordEncoder가 일치여부를 검사하여 인증을 시도합니다.

 

3. BCryptPasswordEncoder

bcrypt 알고리즘

  • 패스워드 해싱에 보편적으로 사용되는 알고리즘입니다.
  • 패스워드 보안의 저항성을 늘리기 위해 의도적으로 많은 연산을 수행합니다.
    • 기본적으로 강도 10으로 셋팅되어 있음
  • bcrypt 알고리즘의 연산 강도를 trade-off를 고려하여 1정도로 맞추는 것이 일반적입니다.

 

4. Change Password Configuration

  • 패스워드 재설정을 위한 Well-Known URL 제공 → 클라이언트는 패스워드 재설정 URL을 쉽게 알 수 있음
  • 기본값: /.well-known/change-password

 

설정

  • 패스워드 재설정 페이지로 리다이렉트 되도록 설정을 제공
더보기
http
    .passwordManagement((management) -> management
        .changePasswordPage("/update-password")
    )

 

 

 

참고