01. Spring security Features 요약 : Password Storage
PasswordEncoder 인터페이스
- 단방향 변환 수행을 도와주는 인터페이스다.
- 이 인터페이스를 활용해서 사용자가 입력한 비밀번호를 인증 시점에서 비교할 수 있다.
Password Storage History
1. 일반 텍스트로 암호 저장
- 이 방식은 SQL 인젝션 등의 악의적인 공격에 너무 취약한 방법이었고, 보안 전문가들로 하여금, 비밀번호를 보호하기 위해선 다른 무언가가 필요하다는 것을 깨닫게 해주었다.
2. SHA-256
- SHA-256과 같은 단방향 해시를 적용하는 것이 관행이었던 시절이 있었다.
- 단방향 해시는, 초기에 설정한 비밀번호를 복호화 할 수 없는 무작위의 문자열의 조합으로 변형 시켜주는 암호화 기법이다.
- 따라서, 인증 시도시, 기존 해시 문자열과 사용자가 입력한 비밀번호의 해시 문자열을 비교한다.
- 그러나 이 방법은 레인보우 테이블에 취약한 암호화 해시 기법이었다.
- 레인보우 테이블에 대비하기 위해 '솔트'라고 하는 무작위의 랜덤 바이트를 기존 비밀번호에 조합하여, 해시처리를 하였는데, 이 방법은 최신 하드웨어의 빨라진 연산속도로 인해 이 암호화 방법 역시 효력을 잃었다.
3. 적응형 단방향 함수(adaptive one-way function)
- 적응형 단방향 함수는 CPU, 메모리 등의 많은 리소스를 소모하여 비밀번호를 검증한다.
- 일부러 연산속도를 늦추는 암호화 기법이기 때문에 기존의 취약점에 어느정도 대비가 가능하다.
- "work factor"를 통해 하드웨어 사양에 맞게 튜닝이 가능하다.
- 튜닝 권장 속도는 비밀번호 검증시도시 1초이다.
DelegatingPasswordEncoder
Password Storage History를 보았다면, 당연히 적응형 단방향 함수가 적용된 PasswordEncoder를 활용하는 것이 맞다. 그러나 다음과 같은 문제점으로, 레거시 버전에는 일반 텍스트 비밀번호를 받는것을 default로 설정했다.
- 옛날 애플리케이션은 마이그레이션이 쉽지 않은 옛 방식으로 비밀번호를 인코딩하고 있다.
- 비밀번호를 저장하기 위한 최선의 관행은 계속 변화한다.
- 프레임워크 특성상 하위 호환성을 보장하지 않는 업데이트를 자주 할 순 없다.
스프링 시큐리티는 DelegatingPasswordEncoder를 도입하여 다음과 같은 방법으로 위 문제를 해결한다.
- 비밀번호를 현재 권장하는 저장 방식으로 인코딩함을 보장한다.
- 검증은 최신 방식과 레거시 방식 모두 지원한다.
- 필요에 따라 인코딩 방식을 변경할 수 있다.
Password Storage Format
DelegatingPasswordEncoder의 일반적인 비밀번호 포맷은 다음과 같다.
{id}encodedPassword
- id는 PasswordEncoder를 식별하는데 사용된다.
- id는 반드시 있어야 하고, {} 안에 포함되어야 한다.
저장 포맷으로 인해 해커에게 비밀번호 인코딩 방식이 노출되는 것을 걱정할 필요가 없다. 저장 포맷은 암호화 알고리즘과는 관계가 없으며, 사실 접두어가 없어도 대부분 암호화 알고리즘은 대부분 쉽게 식별된다.
예를 들자면 Bcrypt 비밀번호는 보통 $2a$로 시작한다.
PasswordEncoder 예시
BcryptPasswordEncoder
비교적 널리 사용되는 암호화 알고리즘으로 의도적으로 느리게 동작하게 하여, 비밀번호 해독을 어렵게 만든다. 대부분의 적응형 단방향 함수와 마찬가지로 시스템에서 비밀번호 하나를 검증하는 데 1초가냥 걸리도록 조정해야 한다.
BcryptPasswordEncoder encoder = new BcryptPasswordEncoder(16); // 파라미터로 강도 조절을 해볼 수 있다.
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
이 외에도 여러가지 적응형 단방향 함수 기반의 passwordEncoder가 있지만, 기존의 PasswordEncoder의 인터페이스를 지키고 있기때문에 코드가 비슷하여, 생략하였다.