이 섹션에서는
Spring Security 안의
PasswordEncoder와
엔드 유저 인증 과정에서
PasswordEncoder의 역할에 대해 다루겠습니다
PasswordEncoder와 더불어
비밀번호를 다루거나
데이터베이스와 같은 저장소에
비밀번호를 저장할 때 준수해야 하는
표준에 대해서도 이야기하겠습니다
PasswordEncoder와 그 세부 내용에
뛰어들기 전에 먼저 여러분에게
Spring Security 안의 고급 PasswordEncoder들을
활용하지 않는 경우
실행되는 기본 로직이
무엇인지 보여드리려고 합니다
일단 아시다시피 우리는 비밀번호를
일반 텍스트 형식으로 취급하는 기본 PasswordEncoder를
사용하고 있습니다
슬라이드를 보시면 가장 첫 단계에서
엔드 유저는 본인의 자격 증명인 유저 네임과
비밀번호를 입력합니다
유저가 로그인 버튼을 클릭하자마자
배경에서
Spring Security 프레임워크는
인증 제공자 내부의 모든 로직을 실행하고
UserDetailsManager의 구현 클래스 내부에 있는
loadUserByUsername 메소드의 도움으로
저장소에서 모든 정보를
불러오려고 할 겁니다
유저 정보를 불러온 다음
Spring Security 프레임워크는
엔드 유저가 제공한 비밀번호와
데이터베이스에서 불러온 비밀번호를 비교합니다
이 비교는 Java에서 두 문자열을 비교할 때처럼
일반 텍스트 비교로 실행되며
equals 메소드의 도움을 받습니다
그와 유사하게 비교를 진행한 후
비밀번호가 일치한다면 로그인에 성공하고
그 반대는 로그인에 실패할 겁니다
하지만 이 접근법에는 문제가 있습니다
우리의 모든 비밀번호는 일반 텍스트 형식으로 저장되어 있습니다
그게 왜 문제인데? 왜 데이터베이스에
비밀번호를 일반 텍스트로 저장할 수 없는데?
라고 하실 수 있습니다
이 접근법의 문제점은
모든 운영 웹 애플리케이션에서
사용하는 데이터베이스의 경우
DBA와 같은 데이터베이스 관리자나
특권 유저가 당연히
운영 데이터베이스에 접근권을 가지며
고객 테이블이나 유저 테이블에
단순히 쿼리를 실행해 웹 애플리케이션에 등록된
모든 고객 또는 엔드 유저의 유저 네임과
비밀번호를 특정할 수 있다는 겁니다
은행 웹 애플리케이션을
개발하는데 모든 고객 비밀번호를
일반 텍스트 비밀번호로 저장하는 시나리오를 생각하면
무슨 일이 일어날지 상상이 가실겁니다
어떤 DBA라도 단순히 비밀번호를
데이터베이스에서 불러와
제 고객의 자격 증명으로 로그인을 시도하고
이를 악용할 수 있습니다
이런 모든 상황을 방지하기 위해
비밀번호를 데이터베이스에 일반 텍스트로 저장하는 것은
절대 추천하지 않습니다
그래서 여기에 이 접근법은
운영에 추천하지 않는다고 강조해둔 것입니다
심각한 무결성과 기밀성
문제가 있기 때문입니다
이 접근법을 사용하는 프로젝트는
고객의 비밀번호가 위험에 노출되었을 때
많은 무결성 및 기밀성 문제를 직면하게 됩니다
여기서는 DBA가 쿼리를 실행한다는
예시를 들었지만 더 나쁜 시나리오를 생각해보겠습니다
예컨대 해커 한 명이 여러분의
데이터베이스 서버에 접근했다고 생각해보면
해커가 모든 비밀번호를
일반 텍스트로 저장해둔 데이터베이스 서버에 접근한다면
이 비밀번호들로 무슨 짓을 할지
상상할 수 없습니다
단순히 악용하거나
경쟁사에 판매할 수도 있습니다
그래서 이 접근법은 절대 추천하지 않습니다
물론 비밀번호를
취급할 때 준수해야 할 다른 접근법이나
표준이 무엇인지 다룰 것이지만
이런 표준이나 우리가 가진 옵션으로
넘어가기 전에
Spring Security 프레임워크 내부의
어디에서 비밀번호 검증이
일어나는지 보여드리고 싶습니다
그러기 위해 로그인한 다음
Spring Security 프레임워크 내부에서
작동하고 있는 비밀번호 검증을 보여드릴 겁니다
그러기 위해 IDEA로 가보겠습니다
여기 DaoAuthenticationProvider 안에서
실질적인 검증이 일어날 겁니다
비밀번호 검증이 일어나고 있는 메소드를
찾아보겠습니다
그러기 위해 Authenticate 메소드로 가보겠습니다
보고 계시는 Authenticate 메소드 안에서
먼저 저장소 또는 데이터베이스에서
유저 네임 정보를 불러오겠습니다
유저 정보를 불러온 다음
PreAuthenticationChecks와 같은
메소드를 실행할 겁니다
PreAuthenticationChecks 내부에서
Spring Security는 저의 계정이
만료되었는지 잠겼는지
비활성화 상태인지 확인합니다
이 모든 부정적인 시나리오를 확인합니다
전부 성공적으로 확인되었으면
additionalAuthenticationChecks라는 메소드를
호출하게 됩니다
이 메소드를 클릭해보면 이 클래스 안의
추상 메소드입니다
그러니 이 메소드는 DaoAuthenticationProvider
내부에 구현된 겁니다
지금 이야기하는 메소드가 이것입니다
이제 이 메소드 안에 중단점을 넣을건데
우리가 사용한 기본 PasswordEncoder로
저의 비밀번호가 어떻게 검증되는지 살펴보겠습니다
그러기 위해서 브라우저로 가서 로그인하겠습니다
myAccount API에
접근 중이고 저의 자격 증명을 요구합니다
입력할 유저 네임은 johndoe@example.com 이고
비밀번호는 54321입니다
로그인을 클릭하면
DaoAuthenticationProvider의
additionalAuthenticationChecks라는 이 메소드 안에서
실행이 멈춘 걸 볼 수 있습니다
먼저 이 메소드를 한 줄씩 디버그해서
무슨 일이 일어나고 있는지 보겠습니다
먼저 저의 인증 객체 내부에
자격 증명이 존재하는지 확인합니다
자격 증명이 존재합니다
자격 증명이 존재한다면
presentedPassword로 여러분의 문자열에 할당됩니다
그러니 인증 객체로부터 받는
비밀번호가 바로 엔드 유저가
입력한 비밀번호입니다
Spring Security 내부 흐름도를 기억하신다면
Spring Security 필터는 유저 네임과
비밀번호를 인증 객체로 변환합니다
그래서 인증 객체로부터 그 비밀번호 정보를
읽어내려고 하는 것입니다
그 다음 여기에서는 PasswordEncoder 내부의
matches라는 메소드를 호출합니다
이 PasswordEncoder를 하이라이트 해보시면
지금 제가 사용하는 PasswordEncoder는
NoOpPasswordEncoder입니다
이 matches 메소드 안에 들어가보겠습니다
여기를 보시면 이것이NoOpPasswordEncoder이고
현재 이 PasswordEncoder의 matches 안에 있습니다
이 Java 주석을 읽어보시면
이 PasswordEncoder는 보호되지 않으며
다른 PasswordEncoder를 사용하라고 합니다
또한 이것은 일반 텍스트 비밀번호로
작업을 선호하는 환경에서 테스트하기
유용하다고 강조합니다
이것이 우리가 지금 사용하고 있는
기본 PasswordEncoder입니다
그래서 이 PasswordEncoder의 matches 메소드
안에 있는 겁니다
이 matches 메소드 내부에서 우리는
엔드 유저로부터 수신한 비밀번호와
데이터베이스로부터 불러온 비밀번호라는
두 문자열의 일치성을 확인하고 있습니다
돌아가보면
두 번째 매개변수는
데이터베이스에서 불러온
userDetails 객체로부터 받고 있습니다
이 두 문자열이 일치한다면
참이라는 불리언을 반환할 것이고
어떤 이유든 자격 증명이 불일치한다면
잘못된 자격 증명이라는
예외를 반환할 것입니다
우리 시나리오에서는 비밀번호가 일치하므로
이 단계로는 가지 않습니다
이 중단점을 해제하고 브라우저로 가보면
성공적인 응답을 받은 것을 볼 수 있습니다
일단은 이것이 우리의 Spring Boot
웹 애플리케이션에서 일어나는 일입니다
보호되지 않은 NoOpPasswordEncoder에
의존하고 있습니다
그래서 이 섹션에서는
비밀번호의 이상적인 표준과
Spring Security 내부의 PasswordEncoder들
그리고 이들을 활용하는 방법을 다룰 겁니다
이것들을 다루고 나면
우리 웹 애플리케이션이 이 섹션에서 다룰
모든 이상적인 표준을 준수하도록
보완할 수 있을 겁니다
이걸로써 이 섹션의 어젠다와
기본 PasswordEncoder인
NoOpPasswordEncoder를 사용할 때 실행되는
흐름을 명확히 이해하셨다고 생각합니다
감사드리며 다음 강의에서 뵙겠습니다
수고하셨습니다