FilterChain (1)

 

 

 

 

 

 

🥧 Spring Security 란?

 

 

🍞 스프링 시큐리티(Spring Security)

 

Acegi Security(아시지 시큐리티) 프로젝트로 시작

➡️ 스프링 포트폴리오 프로젝트에 병합되면서 이름이 Spring Security로 바뀌었다.

 

🍰 Spring Security는 강력하고 커스텀 가능한 인증(Authentication)과 접근 통제(Access Control) 프레임워크이다.

🍰 스프링 기반의 애플리케이션에서 보안을 위한 표준이다.

🍰 모든 자바 애플리케이션에 적용 가능하지만 웹 애플리케이션에서 많이 쓰인다.

🍰 스프링 인터셉터, 필터 기반의 보안 기능을 구현하는 것보다 스프링 시큐리티를 통해 구현하는 것을 권장하고 있다.

🍰 확장성 : 다양한 요구사항을 손쉽게 추가하고 변경할 수 있다.

🍰 실행하려면 Java 8 이상의 환경이 필요

 

 

 

🍞 필수 용어 및 개념

 

🥞 주체 (Principal)

🍰 유저, 기기, 시스템 등이 될 수 있지만 보통 유저(사용자)를 의미한다.

 

🥞 인증 (Authentication)

🍰 특정 리소스에 접근하려고 하는 사용자가 누구인지 확인할 때 사용한다.

🍰 주체의 신원(identity)을 증명하는 과정

🍰 주체는 자신을 인증해달라고 신원 증명 정보(credential)을 제시한다.

🍰 주체가 유저일 경우 신원 증명 정보는 패스워드이다.

 

🥞 인가 (Authorization, 권한 부여)

🍰 인증을 마친 유저에게 권한(authority)을 부여하여 애플리케이션의 특정 리소스에 접근할 수 있게 허가하는 과정

🍰 인가는 반드시 인증 과정 이후 수행되어야 하며 권한은 롤 형태로 부여하는게 일반적이다.

 

🥞 접근통제 (Access control)

🍰 어떤 유저가 애플리케이션 리소스에 접근하도록 허락할 지를 제어하는 행위

🍰 접근 통제 결정(access control decision)이 뒤따른다.

🍰 리소스의 접근 속성과 유저에게 부여된 권한 또는 다른 속성들을 결정한다.

 

 

 


 

 

🥧 Spring Security를 배워야 하는 이유

 

 

🍞 Spring Security 특징

 

🍰 모든 요청에 대해서 인증을 요구한다.

🍰 사용자 이름 및 암호를 가진 사용자가 양식 기반으로 인증할 수 있도록 허용한다.

🍰 사용자의 logout을 허용한다. ➡️ LogoutFilter로 코드의 구현없이도 간편하게 로그아웃을 지원한다. 커스텀도 가능하다.

🍰 CSRF(Cross-site Request Forgery) 공격을 방지한다.

🍰 Session Fixation(세션 고정 공격)을 보호한다.

🍰 보안 헤더 통합

  • HSTS(HTTP Strict Transport Security) 강화
  • X-Content-TypeOptions
  • 캐시 컨트롤(정적 리소스 캐싱)
  • X-XSS-Protection XSS 보안 : 스크립트 공격 보안
  • 클릭재킹을 방지하는 X-Frame 옵션 통합

🍰 Servlet API 제공

 

 

※ Session Fixation (세션 고정 공격)

세션 고정이란 공격자가 유효한 사용자 세션을 가로채도록 허용하는 공격을 말한다.

로그인 시 발급받은 sessionID가 로그인 전후로 모두 동일하게 사용되어 악의적인 사용자가 피해자의 세션을 하이제킹하여 정상적인 사용자로 위장하여 접근한다.

공격자가 유효한 session ID를 얻음 ➡️ 사용자가 공격자의 session ID로 자신을 인증하도록 유도 ➡️ 사용된 session ID를 통해 사용자 검증된 세션을 하이재킹

세션에 대한 만료나 서로 다른 IP의 동일한 세션에 대해 취약한 웹 애플리케이션의 경우 이용하는 다수의 사용자가 해당 공격에 노출될 가능성이 존재한다.

 

참고) OWASP

참고) Session Fixation(세션 고정) 취약점

 

 

※ HSTS(HTTP Strict Transport Security) 기능

HTTP Strict-Transport-Security response header

HTTP 대신 HTTPS만을 사용하여 통신해야한다고 웹사이트가 브라우저에 알리는 보안 기능이다.

통신에서 일부분 HTTP를 사용할 경우 sessionID나 쿠키등이 해커에게 탈취되었을 경우 그대로 데이터가 노출이 되지만 HTTPS를 사용할 경우엔 데이터가 노출되는 것을 막을 수 있다.

만약 웹사이트가 HTTP 요청을 받고 HTTPS로 리다이렉트 하는 경우에, 유저가 http 또는 도메인만 입력한 경우에 리다이렉트 되기 이전의 암호화되지 않은 버전의 사이트와 통신하게 된다. 이경우, 악의적인 다른 페이지로 리다이렉트 되는 man-in-the-middle attack의 잠재적 위험이 있다.

HTTP Strict Transport Security 헤더는 웹사이트가 브라우저에게 절대로 HTTP로 사이트를 연결하면 안되고 HTTP로 연결하려는 모든 시도는 자동으로 HTTPS로 변경해야 한다고 알린다.

 

참고) mdn web docs

참고) HSTS

 

 

※ 클릭재킹

클릭재킹이란 웹 사용자가 자신이 클릭하고 있다고 인자하는 것과 다른 것을 클릭하게 속이는 해킹 기법이다.

공격자는 비밀 정보를 유출시키거나 컴퓨터에대한 제어를 획득할 수 있게 된다.

 

 

※ Cache-Control

header 필드

request와 response 내의 캐싱 정하기 위해 사용된다.

 

 

참고) mdn web docs

 

 


 

 

🥧 Spring Security 환경 구성

 

config 패키지와 SecurityConfig.java 파일 생성

 

  • @Configuration , @EnableWebSecurity 추가
  • @EnableWebSecurity : spring security 필터체인에 등록된다.

 

 

🥞 http.csrf().disable()

form 태그로만 요청이 가능하고 postman 등의 요청이 불가능하게 된다.

 

 

🥞 http.headers().frameOptions().disable()

h2 연결할 때 필요하다.

 

 

🥞 BCryptPasswordEncoder 클래스

PasswordEncoder를 구현한 클래스이고 강력한 해시 함수인 BCrypt 를 사용한다.

 

※ 참고) bcrypt

블로피시 암호에 기반을 둔 암호화 해시 함수

레인보우 테이블 공격 방지를 위해 salt를 추가한 적응형 함수의 하나이다.

 

 

🥞 @EnableGlobalMethodSecurity

Spring Security 의 전역 메서드 보안을 활성화한다.

 

✅ 옵션

1. securedEnabled : Secured 애너테이션 활성화 여부를 결정한다.

2. prePostEnabled : Spring Security의 pre/proAuthorize 애너테이션을 활성화한다.

3. jsr250Enabled : @RoleAllowed 애너테이션을 사용할 수 있도록 해준다.

 

 

🥞 @Secured

1개의 권한을 줄 때 사용

@Secured(“ROLE_ADMIN”)

 

 

🥞 @PreAuthorize

 

1개 이상의 권한을 줄 때 사용

#을 이용해서 파라미터에 접근할 수 있다.

 

@PreAuthorize(“hasRole(‘ROLE_MANAGER’) or hasRole(‘ROLE_ADMIN’)”)

 

@PreAuthozie(“isAuthenticated() and ((#user.name == principal.name) or hasRole(‘ROLE_ADMIN’))”)

 

 

 

🥞 @PostAuthorize

 

메서드가 실행되고 응답하기 직전에 권한을 검사하는데 사용

클라이언트에 응답하기 전에 로그인 상태 또는 반환되는 사용자 이름과 현재 사용자 이름에 대한 검사, 현재 사용자가 관리자 권한을 가지고 있는지 등의 권한 후처리를 한다.

 

 


 

 

🥧 Filter와 FilterChain

 

 

🍞 Filter

 

Spring Filter

 

Spring Security는 Servlet Filter를 기반으로 서블릿을 지원한다.

Filter : HTTP 요청과 응답을 변경할 수 있는 재사용 가능한 코드이다.

 

  • 클라이언트가 서버로 요청을 하게되면 가장 먼저 Servlet Filter를 거치게 된다.
  • Filter를 거치고 나면 Servlet(DispatcherServlet)에서 요청이 처리된다.
  • Spring Security는 주요 보안에 대한 처리를 여러가지 Filter로 처리하도록 구성되어 있다.
  • 인증(Authentication)과 인가(Authorization)에 대한 처리를 Filter에서 한다.
  • 자동 설정 옵션을 사용하면 10개의 스프링 시큐리티 필터가 자동으로 설정된다.

 

 

 

🍞 FilterChain

 

Filter Chain

 

단일 HTTP 요청을 처리하는 전형적인 레이어

여러개의 Filter들이 사슬처럼 연결되어 서로 연결되어 동작한다.

 

 

🥞 FilterChain의 특징

 

🍰 클라이언트가 앱에 요청을 보내고 컨테이너는 요청 URI의 경로를 기반으로 어떤 필터와 어떤 서블릿을 적용할지 결정한다.

🍰 하나의 서블릿은 단일 요청을 처리하지만 필터는 체인을 형성하여 순서를 지정하며 실제로 요청 자체를 처리하려는 경우 필터가 나머지 체인을 거부할 수 있다.

🍰 필터는 다운스트림 필터와 서블릿을 사용해서 요청과 응답을 수정할 수도 있다.

🍰 필터 체인의 순서는 매우 중요하며 Spring Boot는 두 가지 매커니즘을 통해 이를 관리한다. 하나는 Filter 타입의 @Beans 에 @Order를 붙이거나 Ordered를 구현하는 것이고 다른 하나는 API의 일부로 순서를 가지는 FilterRegistrationBean의 일부가 되는 것이다.

🍰 클라이언트는 애플리케이션으로 요청을 전송하고 컨테이너는 Servlet과 여러 Filter로 구성된 FilterChain을 만들어 요청 URI path 기반으로 HttpServletRequest를 처리한다.

🍰 Filter는 요청이 DispatcherServlet에 의해 다뤄지기 전, 후에 동작한다.

🍰 Filter는 FilterChain을 통해 여러 필터가 연쇄적으로 동작하게 할 수 있다.

🍰 1개의 Servlet이 HttpServletRequest와 HttpServletResponse 처리를 담당한다.

  • Filter는 여러개 사용할 수 있다.
  • 다운 스트림의 Servlet과 Filter의 실행을 막는다. (보통 이 경우 Filter에서 HttpServletResonse를 작성한다.)
  • 다운 스트림에 있는 Servlet과 여러 Filter로 HttpServletRequest나 HttpServletResponse를 수정한다.

🍰 Filter는 FilterChain 안에 있을 때 효력을 발휘한다.

 

 

 

🥞 Filter Interface

 

  • public void init(FilterConfig filterConfig) throws ServletException 필터를 웹 컨테이너 내에 생성한 후 초기화 할 때 호출한다.
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException 체인을 따라 다음에 존재하는 필터로 이동한다.
  • 체인의 가장 마지막에는 클라이언트가 요청한 최종 자원이 위치한다.
  • public void destroy() 필터 : 웹 컨테이너에서 삭제될 때 호출된다.
  • doFilter() : 필터의 역할을 하는 메서드

 

 

 

🥞 FilterRegistrationBean 옵션

 

FilterRegistrationBean의 옵션으로 Filter에 여러가지 옵션을 추가할 수 있다.

 

🥑 addUrlPatterns

registrationBean.addUrlPatterns(“/users/*”);

setUrlPatterns와 비슷하다.

/users/ 로 시작하는 url로 요청이 오게되면 필터를 통과한다.

 

🥑 setOrder

registrationBean.setOrder(1);

필터의 실행 순서를 지정한다.

 

 

 

🍞 DelegatingFilterProxy

 

DelegatingFilterProxy

 

Spring Security가 모든 애플리케이션 요청을 감싸게 해서 모든 요청에 보안이 적용되게 하는 ServletFilter이다.

스프링 프레임워크 기반의 웹 애플리케이션에서 서블릿필터 라이프 사이클과 연계해 스프링 빈 의존성을 서블릿 필터에 바인딩하는데 사용한다.

 

  • 스프링 부트는 DelegatingFilterProxy(Filter 구현체)로 서블릿 컨테이너의 생명주기와 스프링 ApplicationContext를 연결한다.
  • Servlet Container는 자체 표준을 사용해서 Filter를 등록할 수 있지만 스프링이 정의하는 Bean은 인식하지 못한다.
  • DelegatingFilterProxy는 표준 서블릿 컨테이너 매커니즘으로 등록할 수 있으면서도 모든 처리를 Filter를 구현한 스프링 빈으로 위임한다.
  • DelegatingFilterProxy는 ApplicationContext에서 Bean Filter0를 찾아 실행한다.
  • Bean Filter0는 FilterChainProxy가 된다.

 

 

 

🍞 FilterChainProxy

 

Spring Security는 FilterChainProxy로 서블릿을 지원한다.

 

FilterChain Proxy

 

  • FilterChainProxy는 Spring Security가 제공하는 특별한 Filter로 SecurityFilterChain을 통해 여러 Filter 인스턴스로 위임할 수 있다.
  • FilterChainProxy는 빈이기 때문에 보통 DelegatingFilterProxy로 감싸져 있다.
  • DelegatingFilterProxy는 서블릿 필터이며, Spring IOC 컨테이너가 관리하는 Filter Bean을 갖고있다.
  • Filter Bean은 FilterChainProxy이며 이 객체안에서 Security와 관련된 일들이 벌어진다고 생각할 수 있다. (위 그림에서의 DelegatingFilterProxy안에 Bean Filter는 FilterChainProxy가 된다.)

 

순수한 Servlet Filter는 본래 Spring Container 외부에 존재한다.

DelegatingFilterProxy 클래스는 Filter를 Spring Bean으로 사용할 수 있다.

DelegatingFilterProxy 클래스(Filter Class)는 Servlet Filter 사이에 존재하고 Spring Bean으로 등록된 Filter에게 처리를 위임한다.

 

 


 

 

🥧 DelegatingPasswordEncoder

 

 

🍞 DelegatingPasswordEncoder 란?

 

Spring Security 5.0 버전 이전에 PasswordEncoder의 기본값인 NoOpPasswordEncoder는 일반 텍스트 비밀번호를 사용했다.

비밀번호 히스토리 세션을 기반으로 PasswordEncoder 기본값이 이제 BCryptPasswordEncoder 같은 것으로 바뀌었다.

하지만 다음의 3가지 문제가 존재한다.

  • 애플리케이션이 마이그레이션(이식)이 쉽지 않은 옛날 방식으로 비밀번호를 인코딩하는 경우
  • 비밀번호를 저장하기 위한 관행이 다시 변경될 수 있음
  • Spring Security는 프레임워크로서 자주 변경사항을 변경할 수 없다.

 

대신에 Spring Security는 위의 문제를 해결할 DelegatingPasswordEncoder를 도입했다.

  • 비밀번호를 최신 저장 권장방법으로 인코딩되었는지 확인한다.
  • 최신 및 레거시 형식의 암호 유효성 검사를 허용한다.
  • 향후 인코딩 업그레이드를 허용한다.

 

 

참고) Baeldung Spring Method Security

참고) Spring Security doc

 

 

 

감사합니다.

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

 

 

1