Dlise
시원한 냉장고
Dlise
전체 방문자
오늘
어제
  • 시원한 냉장고 (132)
    • Java (31)
      • Java (26)
      • Spring (5)
    • Algorithm & PS (25)
      • Algorithm (14)
      • Problem Solving (11)
    • Network (12)
    • Database (2)
    • Data Structure (4)
    • OOP & CleanCode (5)
    • Web (0)
    • Git (2)
    • AI (2)
    • Project (1)
      • Discord Bot (1)
    • Error (19)
    • Tools (5)
    • 수학 (5)
      • 확률과 통계(기초) (5)
    • 컴퓨터 구조 (3)
    • 활동 (16)
      • 행사 & 여행 (6)
      • 자격증 (4)
      • 회고 (6)
      • 기타 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 가장쉬운알고리즘책
  • CleanCode
  • java
  • 네트워크
  • 열혈강의자료구조
  • spring security in action second edition
  • 통계학
  • 백준
  • 중위 표기법
  • 후위 표기법

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Dlise

시원한 냉장고

Java/Spring

Spring Security - PasswordEncoder

2024. 6. 14. 18:28

이번엔 UserDetailsService의 단짝인 PasswordEncoder에 대해 알아보겠다.

PasswordEncoder 형태

PasswordEncoder 모습은 아래와 같다.

public interface PasswordEncoder {

  String encode(CharSequence rawPassword);
  boolean matches(CharSequence rawPassword, String encodedPassword);

  default boolean upgradeEncoding(String encodedPassword) { 
    return false; 
  }
  
}
  • encode(...): 제공된 문자열을 인코딩해 반환
  • matches(...): 인코딩 된 문자열과 기본 비밀번호를 비교
  • upgradeEncoding(...): true를 반환할 시 인코딩된 비밀번호가 다시 인코딩 됨

 

 

PasswordEncoder 구현

이전까지 PasswordEncoder를 아래와 같이 구현해 사용했었다.

@Bean
PasswordEncoder passwordEncoder() {
  return NoOpPasswordEncoder.getInstance();
}

여기서의 NoOpPasswordEncoder는 비밀번호를 encoding 없이 일반 text로 활용하겠다는 것으로 해당 Class의 코드를 확인해 보면 아래와 같다.

@Deprecated
public final class NoOpPasswordEncoder implements PasswordEncoder {

  private static final PasswordEncoder INSTANCE = new NoOpPasswordEncoder();

  private NoOpPasswordEncoder() {
  }

  @Override
  public String encode(CharSequence rawPassword) {
    return rawPassword.toString();
  }

  @Override
  public boolean matches(CharSequence rawPassword, String encodedPassword) {
    return rawPassword.toString().equals(encodedPassword);
  }

  public static PasswordEncoder getInstance() {
    return INSTANCE;
  }

}

encode()와 matches()가 구현되어 있으며 해당 Class는 모든 Password에 대해 동작이 동일하므로 singleton 형태인 것을 확인할 수 있다.

 

PasswordEncoder는 위의 Class 이외에도 Pbkdf2PasswordEncoder, BCryptPasswordEncoder 등 여러 Class를 제공한다.

  • NoOpPasswordEncoder: 비밀번호를 인코딩하지 않고 일반 Text로 사용
  • StandardPasswordEncoder: SHA-256을 사용해 비밀번호를 해시. 
  • Pbkdf2PasswordEncoder: 무차별 대입 공격에 대한 취약성을 줄일 수 있는 방식.
  • BCryptPasswordEncoder: bcrypt 강력한 해싱을 사용해 비밀번호 인코딩
  • SCryptPasswordEncoder: scrypt 해싱 기능을 사용해 비밀번호 인코딩

차근차근 살펴보자.

 

StandardPasswordEncoder

SHA-256을 사용해 비밀번호를 해시한다. 해당 방법은 Deprecated 되었기에 사용하지 않아야 하지만 현재 서비스 중인 Application에서 사용하고 있는 경우가 있다.

 

Pbkdf2PasswordEncoder

Pbkdf2PasswordEncoder의 생성자는 아래와 같다.

여기서 첫 번째 방법은 Deprecated 되었으므로 두 번째 방법을 사용해야 한다.

형태는 아래와 같다.

PasswordEncoder p = new Pbkdf2PasswordEncoder(
        "secret", 16, 310000, 
        Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA256);

키 값, 인코딩에 사용되는 반복 횟수, 해시 크기, 해시 방법 순이다.

secretKeyFactoryAlgoritym은 아래와 같은 옵션이 있다.

  • PBKDF2WithHmacSHA1
  • PBKDF2WithHmacSHA256
  • PBKDF2WithHmacSHA512

숫자가 클수록 강력한 비밀번호가 되지만 성능 측면에선 소비하는 리소스가 많아지므로 상황에 맞게 선택해야 한다.

 

BCryptPasswordEncoder

Bcrypt 강력 해시 기능을 사용하는 방법으로 사용자는 Version(버전), Strength(강도), SecureRandom을 선택할 수 있다.

강도의 값이 클수록 암호를 해시하는 작업이 기하급수적으로 올라간다.

사용은 아래와 같이 한다.

PasswordEncoder p = new BCryptPasswordEncoder();
PasswordEncoder p = new BCryptPasswordEncoder(4);

SecureRandom s = SecureRandom.getInstanceStrong();
PasswordEncoder p = new BCryptPasswordEncoder(4, s);

 

SCryptPasswordEncoder

scrypt 해싱 기능을 사용하며 인자로 5개의 숫자를 받는다.

 

DelegatingPasswordEncoder

DelegatingPasswordEncoder는 다른 PasswordEncoder와 사용 방법이 다르다.

 

어떤 Application은 상황에 따라 다른 PasswordEncoder를 사용하고 싶을 수 있다.

혹은 기존에 사용하던 password encoding 방법에서 취약점이 발견되어 다른 방법으로 변경해야 하는 경우 가 있을 수 있다.

이러한 경우 DelegationPasswordEncoder를 사용하는 것이 좋다.

 

DelegatingPasswordEncoder는 인코딩 알고리즘을 구현하지 않고 다른 PasswordEncoder 구현체에 인코딩을 위임한다. 인코딩 된 해시는 사용된 알고리즘의 이름이 prefix로 시작한다.(중괄호 필수)

  • {noop}
  • {bcrypt}
  • {pbkdf2}
  • ...

DelegatingPasswordEncoder는 접두사를 확인해 알맞은 PasswordEncode 구현체에 작업을 위임한다.

만약 해시된 비밀번호가  "{noop}ABCDE"라면 DelegatingPasswordEncoder는 {noop} prefix를 확인해 NoOpPasswordEncoder에게  작업을 위임한다.

 

활용법은 아래와 같다.

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;

import java.util.HashMap;
import java.util.Map;

public class DelegatingPasswordEncoderExample {

    public static void main(String[] args) {
        // 여러 인코더를 등록
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
        encoders.put("bcrypt", new BCryptPasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        
        // 기본 인코더를 bcrypt로 설정
        PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder("bcrypt", encoders);
        
        // 패스워드 인코딩
        String encodedPassword = passwordEncoder.encode("myPassword");
        System.out.println("Encoded Password: " + encodedPassword);
        
        // 패스워드 매칭
        boolean matches = passwordEncoder.matches("myPassword", encodedPassword);
        System.out.println("Password matches: " + matches);
    }
}

만약 해시한 값에 접두사가 없으면 기본 인코더를 사용한다. 기본 인코더는 인스턴스를 생성할 때 지정된 첫 번째 인코더이다.

 

Spring Security는 DelegatingPasswordEncoder를 편리하게 생성할 수 있도록 PasswordEncoderFactories 클래스에서 createDelegatingPasswordEncoder() method를 제공한다.

해당 method는 모든 표준 PasswordEncoder 매핑이 되어있고 bcrypt를 기본 인코더로 하는 DelegatingPasswordEncoder를 반환한다.

PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

'Java > Spring' 카테고리의 다른 글

Spring Security - SSCM  (2) 2024.06.15
Spring Security - UserDetails, UserDetailService  (1) 2024.06.12
Spring Security - Spring Security 겉핥기  (0) 2024.06.11
Spring Security - Spring Security를 공부하는 이유  (1) 2024.06.04
    'Java/Spring' 카테고리의 다른 글
    • Spring Security - SSCM
    • Spring Security - UserDetails, UserDetailService
    • Spring Security - Spring Security 겉핥기
    • Spring Security - Spring Security를 공부하는 이유
    Dlise
    Dlise

    티스토리툴바