DelegatingPasswordEncoder
DelegatingPasswordEncoder는 Spring Security에서 지원하는 PasswordEncoder 구현 객체를 생성해 주는 컴포넌트로써 DelegatingPasswordEncoder를 통해 애플리케이션에서 사용할 PasswordEncoder를 결정하고, 결정된 PasswordEncoder로 사용자가 입력한 패스워드를 단방향으로 암호화해준다.
// PasswordEncoder 객체 생성
PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
// PasswordEncoder 사용
passwordEncoder.matches((String) credentials,password)
위와 같이 PasswordEncoderFactories.createDelegatingPasswordEncoder();를 통해 DelegatingPasswordEncoder의 객체를 생성하고, 내부적으로 DelegatingPasswordEncoder가 다시 적절한 PasswordEncoder 객체를 생성한다.
이후 matches()메서드를 이용하면 UsernamePasswordAuthenticationToken의 password를 단방향 암호화한 뒤 UserDetails의 credential과 비교한다.
Custom DelegatingPasswordEncoder 생성
Spring Security에서 지원하는 PasswordEncoderFactories 클래스를 이용하면 기본적으로 Spring Security에서 권장하는 PasswordEncoder를 사용할 수 있지만 필요한 경우, DelegatingPasswordEncoder로 직접 PasswordEncoder를 지정해서 Custom DelegatingPasswordEncoder를 사용할 수 있다.
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(idForEncode, encoders);
암호화 기술
해시(Hash) 알고리즘
해시 알고리즘은 단방향 암호화를 위한 핵심 알고리즘이다. 단방향 암호화라는 용어에서도 그 특성이 잘 드러나듯이 한번 암호화되면 복호화되기 어려운 특성을 가지고 있다.
데이터베이스에 암호화되어 저장되는 패스워드 자체는 사용자가 입력한 패스워드와 비교해 올바른 패스워드를 입력했는지 검증하는 용도이기 때문에 다시 복호화될 필요가 없다.
MD5(Message Digest 5)
MD5는 초창기에 사용하던 MD2, MD4 해시 알고리즘의 결함을 보완한 알고리즘이다. 하지만 MD5 역시 단방향 알고리즘인데도 불구하고 복호화가 된 사례가 종종 발견되어 지금은 거의 사용하지 않는 알고리즘이다.
다이제스트(Digest) - 원본 메시지를 암호화한 메시지를 의미
SHA(Secure Hash Algorithm)
MD5의 결함을 보완하기 위해서 나온 대표적인 해시 알고리즘이 SHA 알고리즘이다. SHA 알고리즘은 해시된 문자열을 만들어내기 위해 비트 회전 연산이 추가된 방식이다. 쉽게 말해서 해시된 문자열의 비트 값을 회전하면서 반복적으로 해시 처리를 하는 것이다.
반복적으로 해시처리를 하여도 Rainbow Table을 이용한 RainbowAttack으로 해킹의 위험이 존재한다.
RainbowAttack
사용자가 패스워드로 사용할만한 문자열들을 미리 목록(Rainbow Table)으로 만들어 놓고, 이 목록에 있는 문자열을 동일한 알고리즘으로 암호화한 후, 탈취한 암호화된 문자열과 서로 비교하는 작업을 통해 패스워드의 원본 문자열을 알 수 있게 되는데, 이러한 공격을 Rainbow Attack이라고 한다.
Rainbow Attack에 대한 대응책
가장 단순한 방법은 앞에서 살펴본 SHA 알고리즘처럼 해시된 다이제스트를 또 해시하고, 또 해시된 다이제스트를 반복적으로 해시하는 것입니다(이를 키 스트레칭이라고 합니다). 해시 처리가 반복되면 될수록 다이제스트(Digest)를 비교하는 횟수도 현저히 줄어든다.
또 한 가지 방법은 솔트(Salt)를 이용하는 방법이다.
솔트(Salt)란 패스워드로 입력하는 원본 메시지에 임의의 어떤 문자열을 추가해서 해시 처리하는 것을 의미한다.
솔트(Salt)를 추가하면 Rainbow Table을 이용해 비교해야 하는 경우의 수가 늘어나기 때문에 완벽하지는 않지만 Rainbow Attack에 대응할 수 있다.
Work Factor를 추가한 Hash 알고리즘
해시 알고리즘을 연구하는 사람들의 고민 중 하나는 공격자가 Rainbow Attack과 같은 공격을 통해 해시된 메시지를 알아내려고 시도하더라도 어떻게 하면 최대한 느리게 최대한 비용이 많이 들게 할 수 있을까 이다.
이런 고민을 통해서 탄생한 Hash 알고리즘이 PBKDF2, bcrypt, scrypt이다.
여기서 말하는 Work Factor는 공격자가 해시된 메시지를 알아내는 데 더 느리게 더 비용이 많이 들게 해주는 특정 요소를 의미한다.
PBKDF2나 bcrypt의 경우 Work Factor로 솔트와 키 스트레칭을 기본적으로 사용하지만 내부적으로 훨씬 복잡한 알고리즘을 이용해서 공격자의 공격을 느리게 만든다.
scrypt는 기본적으로 다이제스트 생성 시, 메모리 오버헤드를 갖도록 설계되어 있기 때문에 무차별 대입 공격(Brute Force Attack)을 시도하기 위해 병렬화 처리가 매우 어려운 특징이 있다.
'Java > Spring Security' 카테고리의 다른 글
CORS 설정 (0) | 2023.06.07 |
---|---|
JWT (0) | 2023.05.17 |
SecurityFilterChain과 AuthenticationProvider 구현 (0) | 2023.05.13 |
Spring Security의 인증 처리 흐름 (0) | 2023.05.13 |
Servlet Filter 구현 (0) | 2023.05.13 |