Authentication (1)

 

 

 

 

 

 

🍙 Spring Security의 인증 처리 흐름

 

 

Authentication flow

 

 


 

 

🍙 Authentication(인증)

 

 

🍘 Authentication 인터페이스

 

🍥 Spring Security에서 Authentication은 인터페이스로 존재한다.

🍥 사용자가 인증을 성공적으로 수행하면 사용자의 인증정보를 가지고 있는다.

🍥 SecurityContextHolder는 SecurityContext를 호출하고 SecurityContext는 Authentication 객체를 갖고 있다.

🍥 일반적으로 많이 사용하는 구현체는 UsernamePasswordAuthenticationToken 이다.

➡️ 사용자의 고유 식별자(Username)과 암호(Password)로 간단하게 Authentication 객체를 생성할 수 있다.

 

 

🍘 Spring Security내에서의 Authentication 주요 목적

 

  • 사용자가 인증을 위해 제공한 Credential(자격증명)을 제공하기 위해 AuthenticationManager에 대한 입력값이다.
  • 현재 인증된 사용자를 나타낸다. 현재 Authenticaion은 SecurityContext에서 얻을 수 있다.

 

 

 

🍘 Authentication 에 포함된 정보

 

🍰 principal

  • 사용자를 식별한다.
  • UserDetails 인터페이스 구현체는 사용자의 username과 password로 인증이 이루어진다.
  • 구현체 위치 : org.springframework.security.core.userdetails.User
  • 직접 UserDetails를 상속받아 구현할 수도 있다.

 

🍰 credentials

  • 대부분 password 이다.
  • 대부분의 경우 사용자 안증 후에 유출되지 않도록 삭제된다.

 

🍰 authorities

  • AuthenticationManager에 의해 부여된 인가에 대한 정보이다.
  • 부여된 권한에 대한 정보는 GrantedAuthority로 추상화한다.
  • 일반적으로 사용하는 구현체는 SimpleGrantedAuthority 이다.

 

 

 

🍘 Spring Security 에서 Authentication

 

  • Spring Security 는 종합적인 인증처리를 지원한다.
  • 인증은 특정 리소스에 접근하려고 하는 사용자가 누구인지 확인할 때 사용한다.
  • 보통 사용자가 이름과 비밀번호를 입력하는 것으로 사용자를 인증한다.
  • 한번 인증하면 사용자를 식별하고 권한을 부여할 수 있다. (Authorization, 인가)

 

 

 

🍘 Password Storage

 

Spring Security의 PasswordEncoder 인터페이스는 비밀번호를 안전하게 저장할 수 있도록 단방향 변환을 수행해준다.

  • PasswordEncoder 는 비밀번호를 단반향(비밀번호 -> 암호화)으로 변환, 양방향 변환 목적 x
  • 인증에 사용할 credential 정보를 저장한다.

PasswordEncoder 를 사용해서 저장하는 비밀번호는 인증 시점에 사용자가 입력하는 비밀번호와 비교하는 용도이다.

 

 

 

🍘 Password Storage History

 

패스워드를 저장하는 표준 매커니즘의 변화

 

🍰 처음에는 일반 텍스트로 암호를 저장했다.

  • 비밀번호를 담고 있는 DB에 접근하려면 credential이 필요했기 때문에 비밀번호가 안전하다고 생각했다.
  • 악의적인 SQL Injection 같은 공격으로 “Data dump”를 읽어갈 수 있는 방법이 존재한다.

 

🍰 암호를 저장하기 전에 SHA-256 같은 단방향 해시를 실행한 후 암호를 저장했다.

  • 사용자가 인증을 시도할 때 입력한 암호를 해시 처리하여 저장된 비밀번호와 입력한 비밀번호의 해시값을 비교한다.
  • 시스템에선 비밀번호의 단방향 해시만 저장하면 됐다.
  • 악의적인 공격으로 정보가 유출이 되더라도 실제 비밀번호가 아닌 비밀번호의 단방향 해시값만 알 수 있다.
  • 단방향 해시값만으로 비밀번호를 추측하기는 불가능에 가까웠다.
  • 하지만 레인보우 테이블로 알려진 룩업 테이블이 만들어지면서 안전하지 않게 됐다.

 

🍰 레인보우 테이블을 무력화하기 위해 솔티드 패스워드(salted password)를 사용했다.

  • 비밀번호 해시 함수 ➡️ 모든 사용자의 비밀번호로 랜덤 바이트(솔트)를 만들었다.
  • 솔트와 사용자의 비밀번호로 해시 함수를 실행하면 유니크한 해시값을 생성한다.
  • 솔트는 유니크하기 때문에 솔트와 비밀번호 조합은 레인보우 테이블을 무력화할 수 있었다.
  • 하지만 기술이 발전함에 따라 최신 하드웨어를 사용하면 초당 수십억 건을 계산할 수 있게 되며 비밀번호를 쉽게 해독할 수 있어 SHA-256같은 해시가 더 이상 안전하지 않게 되었다.

 

🍰 현재는 적응형 단방향 함수(adaptive one-way function)로 비밀번호를 저장하는 것을 권장한다.

  • CPU, 메모리 등 많은 리소스를 소모해서 비밀번호를 검증 ➡️ 애플리케이션 성능이 크게 떨어질 수 있다.
  • 적응형 단방향 함수는 하드웨어 사양에 따라 work factor를 구성할 수 있다.
  • 워크 팩터(work factor) : 공격자가 암호를 깨는데 드는 노력, 시스템에서 비밀번호를 검증할 때 소요되는 시간과 관련있다.
  • 공격자가 쉽게 비밀번호를 해독하지 못하게 만들지만 시스템 자체에 부담이 된다.
  • Spring Security 는 work factor 시작점을 제공하지만 성능에 따라 각자의 시스템에 맞게 설정하는게 좋다.
  • 종류 : bcrypt, PBKDF2, scrypt, argon2
  • 사용하는 부분에서 장기 자격 증명(사용자 이름및 암호)에서 단기 자격 증명(세션, OAuth 토큰 등)으로 바꾸는게 좋다. ➡️ 단기 자격 증명은 동일한 보안 수준을 유지하면서도 빠르게 검증할 수 있다.

 

 


 

 

🍙 Servlet Authentication Architecture

 

 

🍘 Architecture

 

Servlet authentication에서 사용하는 Spring Security의 구성요소

 

🍰 SecurityContextHolder

Spring Security가 인증한 대상에 대한 디테일을 저장하는 곳이다.

SpringContextHolder

 

 

🍰 SecurityContext

SecurityContextHolder에서 가져와진다. 현재 인증된 사용자의 인증을 포함한다.

 

 

🍰 Authentication

AuthenticationManager의 입력으로 사용된다.

사용자 인증을 위해 제공한 credential이나 SecurityContext에 있는 현재 사용자의 credential을 제공한다.

Authentication이 가지고 있는 정보들

  • principal : 사용자를 식별한다. username/password로 인증할 때 주로 UserDetails의 인스턴스를 이용한다.
  • credentials : 일반적으로 password를 사용한다. (유출되지 않도록 인증 후 비운다.)
  • authorities : 사용자에게 부여한 권한은 grantedAuthority로 추상화한다.

 

 

🍰 GrantedAuthority

Authentication에서 principal(주체)에게 부여된 authority(권한)이다. (역할, 범위 등)

Authentication.getAuthorities() 메서드는 GrantedAuthority 객체들을 Collection으로 리턴한다.

권한은 보통 역할(roles)이다. (예 : "ROLE_ADMINISTRATOR", "ROLE_HR_SUPERVISOR")

이런 역할들은 나중에 웹 권한 부여, 메서드 권한 부여 및 도메인 객체 권한 부여를 위해 구성된다.

username/password 기반 인증을 사용한다면 UserDetailsServiceGrantedAuthority를 로드한다.

GrantedAuthority 객체는 애플리케이션 전체에 걸친 권한을 의미한다. (특정 도메인으로 한정하는 목적으로 사용하진 않는다.)

 

 

🍰 AuthenticationManager

Spring Seuciry의 필터가 어떻게 인증을 수행하는지에 대한 방법을 정의하는 API이다.

  • AuthenticationManager를 호출한 객체(Filter)가 리턴한 AuthenticationSecurityContextHolder에 설정한다.
  • Spring Security의 Filters를 사용하지 않을 경우 AuthenticationManager를 사용하지 않고 SecurityContextHolder에 직접 설정하면 된다.
  • AuthenticationManager의 가장 많이 사용하는 구현체는 ProviderManager이다.

 

 

🍰 ProviderManager

AuthenticationManager의 가장 일반적인 구현체이다.

  • ProviderManager는 동작을 AuthenticationProvider List에 위임한다.
  • 모든 AuthenticationProvider는 인증을 성공 or 실패 or 결정을 내릴 수 없는 지를 판단하고 다운스트림에 있는 AuthenticationProvider가 결정하게 할 수 있다.
  • 설정한 AuthenticationProvider들 중 어떤 것도 인증할 수 없다면, ProviderNotFoundException 오류와 함께 인증이 실패한다.
  • ProviderNotFoundException : ProviderManager가 전달된 Authentication 유형을 지원하도록 구성되지 않았음을 나타낸다.
  • AuthenticationProvider마다 각자 맡은 인증을 수행한다.

 

ProviderManager

 

  • ProviderManager에 인증을 수행할 AuthenticatinoProvider가 없을 경우, 부모 AuthenticationManager 구성을 허용한다.
  • 부모는 어떤 타입의 AuthenticationManager든 가능하지만 주로 ProviderManager의 인스턴스이다.

 

ProviderManager - AuthenticationManager

 

  • 여러 개의 SecurityFilterChain 인스턴스가 공통의 인증(같은 부모 AuthenticationManager)을 가지지만 다른 인증 매커니즘(다른 ProviderManager 인스턴스)일 경우 같은 부모 AuthenticationManager를 여러 ProviderManager가 공유할 수 있다.

 

multiple ProviderManager

 

  • ProviderManager는 인증 이후의 리턴된 Authentication 객체의 모든 민감한 credential 정보는 제거하려고 시도한다.
  • password와 같은 정보가 HttpSession에 필요 이상으로 오래 남아있는 것을 방지하기 위해서이다.
  • 하지만 Authentication에 cache의 객체(ex. UserDetails)에 대한 참조가 포함되어 있는 경우 credential 정보를 모두 제거하면 캐시된 값에 대해 더 이상 인증할 수 없는 문제가 생길 수 있다.
  • 해결책은 캐시 구현 또는 반환된 Authentication 객체를 생성하는 AuthenticationProvider에서 먼저 객체의 복사본을 만드는 것이다.
  • 또 다른 해결책으로는 ProviderManagereraseCredentialsAfterAuthentication property를 비활성화 하는것이다.

 

 

🍰 AuthenticationProvider

다수의 AuthenticationProviderProviderManager에 주입할 수 있다.

각각의 AuthenticationProvider는 특정 유형의 인증을 수행한다.

ex) DaoAuthenticationProvider : username/password 기반의 인증을 지원한다.

JwtAuthenticationProvider : JWT 토큰 인증을 지원한다.

 

 

🍰 Request Credentials with AuthenticationEntryPoint

AuthenticationEntryPoint는 클라이언트의 자격증명 요청에 대한 응답 HTTP를 보내는데 사용된다.

또한 클라이언트에 자격증명을 요청할 때 사용된다.

AuthenticationEntryPoint의 구현체는 로그인 페이지로 리디렉션하거나 WWW-Authenticate header를 전송하는 것을 수행한다.

 

 

🍰 AbstractAuthenticationProcessingFilter

사용자의 credentials를 인증하기 위한 기초 Filter로 사용된다.

credentials이 인증되기 전에, Spring Security는 일반적으로 AuthenticationEntryPoint를 사용하여 credentials을 요청한다.

여러 component를 조합하여 심도있는 인증 플로우를 구성할 수 있다.

 

 

 

 

참고) Servlet Authentication Architecture

참고) Spring Security doc

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😃

 

 

 

1