백엔드 데브코스 TIL 42일차

Inchang Choi·2022년 5월 26일
0

백엔드 데브코스 TIL

목록 보기
26/30
post-thumbnail

학습목표

강의를 들으며 내가 알고 있는 내용을 점검하고,

새로 배운 내용을 정리하며,

궁금한 내용을 알아가며 학습해나가는 것을 목표로 합니다.

RememberMeAuthenticationFilter

인증되지 않은 사용자의 HTTP 요청이 remember-me 쿠키(Cookie)를 갖고 있다면, 사용자를 자동으로 인증처리 하는 필터입니다.

  • key : remember-me 쿠키에 대한 고유 식별 키이며 미입력시 자동으로 랜덤 텍스트가 입력 됩니다.
  • rememberMeParameter : remember-me 쿠키 파라미터명 (기본값 remember-me)
  • tokenValiditySeconds : 쿠키 만료 시간 (초 단위)
  • alwaysRemember : 항상 remember-me 를 활성화 시킴 (기본값 false)

실제 사용자 인증은 RememberMeServices 인터페이스 구현체를 통해 처리됩니다.

TokenBasedRememberMeServices

MD5 해시 알고리즘 기반 쿠키 검증을 담당합니다.

PersistentTokenBasedRememberMeServices

외부 데이터베이스에서 인증에 필요한 데이터를 가져오고 검증합니다.

사용자마다 고유의 Series 식별자가 생성되고, 인증 시 마다 매번 갱신되는 임의의 토큰 값을 사용하여 보다 높은 보안성을 제공합니다.

RememberMeAuthenticationToken

remember-me 기반 Authentication 인터페이스 구현체입니다.

RememberMeAuthenticationToken 객체는 언제나 인증이 완료된 상태에만 존재합니다.

RememberMeAuthenticationProvider

RememberMeAuthenticationToken 기반 인증 처리를 위한 AuthenticationProvider입니다.

앞서 remember-me 설정 시 입력한 key 값을 검증합니다.

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
	if (!supports(authentication.getClass())) {
		return null;
	}
	if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
		throw new BadCredentialsException(this.messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
				"The presented RememberMeAuthenticationToken does not contain the expected key"));
	}
	return authentication;
}

명시적인 로그인 아이디/비밀번호 기반 인증 사용와 권한을 구분합니다.

remember-me 기반 인증과 로그인 아이디/비밀번호 기반 인증 결과가 명백히 다르다는 것에 주목해야 합니다.

  • remember-me 기반 인증 결과 → RememberMeAuthenticationToken
  • 로그인 아이디/비밀번호 기반 인증 결과 → UsernamePasswordAuthenticationToken
  • remember-me 기반 인증은 로그인 기반 인증 보다 보안상 다소 약한 인증
  • 따라서, 모두 동일하게 인증된 사용자라 하더라도 권한을 분리할 수 있습니다.
  • isFullyAuthenticated : 명시적인 로그인 아이디/비밀번호 기반으로 인증된 사용자만 접근 가능
@Override
public final boolean isFullyAuthenticated() {
	return !this.trustResolver.isAnonymous(this.authentication)
			&& !this.trustResolver.isRememberMe(this.authentication);
}

세션 처리

SecurityContextPersistenceFilter

SecurityContextRepository 인터페이스 구현체를 통해 사용자의 SecurityContext를 가져오거나 갱신합니다.

인증 관련 필터 중 가장 최상단에 위치합니다.

이미 인증된 사용자는 다시 로그인할 필요가 없습니다.

SecurityContext가 존재하지 않는다면, empty SecurityContext를 생성합니다.

SecurityContextRepository 인터페이스 기본 구현은 Session을 이용하는 HttpSessionSecurityContextRepository 클래스입니다.

SessionManagementFilter

세션 고정 보호 (session-fixation protection)

session-fixation attack — 세션 하이재킹 기법중 하나로 정상 사용자의 세션을 탈취하여 인증을 우회하는 기법

  • 인증 전에 발급 받은 세션 ID가 인증 후에도 동일하게 사용되면 발생할 수 있습니다.
  • 즉, 인증 전에 사용자가 가지고 있던 세션이 인증 후에는 사용되지 않도록 하면 해당 공격에 효과적으로 대응할 수 있습니다.

Spring Security에서는 4가지 설정 가능한 옵션을 제공합니다.

  • none : 아무것도 하지 않음 (세션을 그대로 유지함)
  • newSession : 새로운 세션을 만들고, 기존 데이터는 복제하지 않음
  • migrateSession : 새로운 세션을 만들고, 데이터를 모두 복제함
  • changeSession : 새로운 세션을 만들지 않지만, session-fixation 공격을 방어함 (단, servlet 3.1 이상에서만 지원)

유효하지 않은 세션 감지 시 지정된 URL로 리다이렉트 시킵니다.

세션 생성 전략 설정

  • IF_REQUIRED : 필요시 생성함 (기본값)
  • NEVER : Spring Security에서는 세션을 생성하지 않지만, 세션이 존재하면 사용함
  • STATELESS : 세션을 완전히 사용하지 않음 (JWT 인증이 사용되는 REST API 서비스에 적합)
  • ALWAYS :항상 세션을 사용함
  • 동일 사용자의 중복 로그인 감지 및 처리
    • maximumSessions: 동일 사용자의 최대 동시 세션 갯수
    • maxSessionsPreventsLogin: 최대 갯수를 초과하게 될 경우 인증 시도를 차단할지 여부 (기본값 false)
@Override
protected void configure(HttpSecurity http) throws Exception {
  http
		/**
     * 세션 관련 설정
     */
    .sessionManagement()
      .sessionFixation().changeSessionId()
      .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
      .invalidSessionUrl("/")
      .maximumSessions(1)
	      .maxSessionsPreventsLogin(false)
				.and()
      .and()
  ;
}

AbstractAuthenticationProcessingFilter 객체는 SessionManagementFilter와 동일한 세션 고정 보호, 최대 로그인 세션 제어를 수행합니다.

  • 위 두 개의 필터는 SessionAuthenticationStrategy 객체를 공유합니다.
  • AbstractAuthenticationProcessingFilter 구현을 보면, 인증 처리가 완료 된 후 SessionAuthenticationStrategy 객체를 통해 필요한 처리를 수행하고 있습니다.

인가(Authorization) 처리

인가(Authorization)란 어플리케이션 보안을 이해하는데 두 번째로 중요한 핵신 개념으로(다른 하나는 인증) 권한이 부여된 사용자들만 특정 기능 또는 데이터에 접근을 허용하는 기능입니다.

이를 위해 인가 처리는 두 개의 작업으로 구분됩니다.

  • 인증된 사용자와 권한을 매핑해야 합니다. Spring Security에서는 보통 역할이라고 합니다. (예: ROLE_USER, ROLE_ADMIN, ROLE_ANONYMOUS)
  • 보호되는 리소스에 대한 권한 확인이 필요합니다. 예를 들면
    관리자 권한을 가진 사용자만 관리자 페이지에 접근 가능 합니다.

Spring Security 3.0 — Dmitry Noskov

FilterSecurityInterceptor

  • 필터 체인 상에서 가장 마지막에 위치하며, 사용자가 갖고 있는 권한과 리소스에서 요구하는 권한을 취합하여 접근을 허용할지 결정합니다.
  • 실질적으로 접근 허용 여부 판단은 AccessDecisionManager 인터페이스 구현체에서 이루어집니다.
  • 해당 필터가 호출되는 시점에서 사용자는 이미 인증이 완료되고, Authentication 인터페이스의 getAuthorities() 메소드를 통해 인증된 사용자의 권한 목록을 가져올수 있습니다.
    • 익명 사용자도 인증이 완료된 것으로 간주하며, ROLE_ANONYMOUS 권한을 갖습니다.
  • 보호되는 리소스에서 요구하는 권한 정보는 SecurityMetadataSource 인터페이스를 통해 ConfigAttribute 타입으로 가져옵니다.

AccessDecisionManager 인터페이스

사용자가 갖고 있는 권한과 리소스에서 요구하는 권한을 확인하고, 사용자가 적절한 권한을 갖고 있지 않다면 접근 거부 처리합니다.

  • AccessDecisionVoter 목록을 갖고 있습니다.
  • AccessDecisionVoter들의 투표(vote)결과를 취합하고, 접근 승인 여부를 결정하는 3가지 구현체를 제공합니다.
    • AffirmativeBased : AccessDecisionVoter가 승인하면 이전에 거부된 내용과 관계없이 접근이 승인됩니다. (기본값)
    • ConsensusBased : 다수의 AccessDecisionVoter가 승인하면 접근이 승인됩니다.
    • UnanimousBased : 모든 AccessDecisionVoter가 만장일치로 승인해야 접근이 승인됩니다.

AccessDecisionVoter 인터페이스

각각의 AccessDecisionVoter는 접근을 승인할지 거절할지 혹은 보류할지 판단합니다. (vote 메소드)

int ACCESS_GRANTED = 1;
int ACCESS_ABSTAIN = 0;
int ACCESS_DENIED = -1;

int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
  • ACCESS_GRANTED : 접근 승인
  • ACCESS_DENIED : 접근 거부
  • ACCESS_ABSTAIN : 판단 보류 (판단을 위한 정보 부족 등)

WebExpressionVoter 구현체

  • SpEL 표현식을 사용해 접근 승인 여부에 대한 규칙을 지정할 수 있습니다.
  • SpEL 표현식 처리를 위해 DefaultWebSecurityExpressionHandler 그리고 WebSecurityExpressionRoot 구현에 의존합니다.
  • DefaultWebSecurityExpressionHandler.createSecurityExpressionRoot() 메소드에서 WebSecurityExpressionRoot 객체를 생성합니다.
  • WebSecurityExpressionRoot 클래스는 SpEL 표현식에서 사용할수 있는 다양한 메소드를 제공합니다.

SpEL 표현식 커스텀 핸들러 구현

SpEL 표현식을 구현할 때 expressionHandler를 제대로 설정하지 않으면 ExpressionUtils.evaluateAsBoolean() 메소드에서 예외가 발생합니다.

  • DEBUG 로그 레벨로는 확인할 수 있으며, ExpressionUtils 클래스에 브레이크 포인트를 걸어두고 확인할 수 있습니다.

⚠️ org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'oddAdmin' cannot be found on object of type 'org.springframework.security.web.access.expression.WebSecurityExpressionRoot' - maybe not public or not valid?

profile
always positive

0개의 댓글