본문 바로가기

Spring/Security

Spring Security 5: Security Filter #4

인증 이벤트

인증 성공 또는 실패가 발생했을 때 관련 이벤트(ApplicationEvent)가 발생하고, 해당 이벤트에 관심있는 컴포넌트는 이벤트를 구독할 수 있다.

  • AuthenticationEventPublisher
    • 인증 성공 또는 실패가 발생했을 때 이벤트를 전달하기 위한 이벤트 퍼블리셔 인터페이스
    • 기본 구현체로 DefaultAuthenticationEventPublisher 클래스가 사용됨
  • AuthenticationSuccessEvent — 로그인 성공 이벤트
  • AbstractAuthenticationFailureEvent — 로그인 실패 이벤트 (실패 이유에 따라 다양한 구체 클래스가 정의되어 있음, 각 구현체는 클래스 내에서 하위 클래스를 보면 알 수 있음)

이벤트 사용 방법은 @EventListner 사용하고 이벤트 객체 파라미터로 받으면 됨
비동기로 사용하려면 @Configuration 클래스나 main 클래스에 @EnableAsync를 쓰고, @EventListner 위에 @Async를 붙여주면 됨

예시 코드

@Component
public class CustomAuthenticationEventHandler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @EventListener
    @Async // 비동기로 작동 인증 과정과 별개로 다른 스레드에서 해당 메소드 작동함
    public void handleAuthenticationSuccess(AuthenticationSuccessEvent event) {
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        var authentication = event.getAuthentication();

        logger.info("User {} login success", authentication.getPrincipal());
    }

    @EventListener
    public void handleAuthenticationFailure(AbstractAuthenticationFailureEvent event) {
        var e = event.getException();
        var authentication = event.getAuthentication();

        logger.warn("User {} login failure", authentication.getPrincipal(), e);
    }
}

@SpringBootApplication
@EnableAsync
public class SsmcApplication {

    public static void main(String[] args) {
        SpringApplication.run(SsmcApplication.class, args);
    }
}

그 밖의 필터들

HeaderWriterFilter

  • 응답 헤더에 보안 관련 헤더를 추가함
    • 관련 이슈에 대해 기본적인 방어 기능만 제공하는것으로 완벽하게 방어되지 않음
    • 또한 브라우저마다 다르게 동작할 수 있으므로 유의해야함

1. XContentTypeOptionsHeaderWriter — MIME sniffing 공격 방어

  • MIME type은 웹 브라우저가 웹 서버로부터 받은 데이터의 MIME 타입을 결정하는 방법 중 하나다. MIME 타입은 인터넷에서 파일의 종류를 설명하는 방법으로, "text/html", "image/jpeg", "application/json" 등이 있다.
  • 브라우저에서 컨텐츠를 mime타입 형식외의 형식으로 해석하는것을 제한한다.

예를 들어, 웹사이트에서 사용자들이 이미지나 다른 파일을 업로드하는 기능을 제공할 때, 이러한 기능이 제대로 보호되지 않는다면 공격자가 이를 악용하여 악성 스크립트를 업로드하는 상황이 발생할 수 있다. 이것은 보안 취약점이며, 공격자가 악성 코드를 웹사이트에 주입하는 여러 가지 공격 중 하나인 "파일 업로드 취약점"에 해당한다.

 

이런 상황에서, 웹 서버가 그 파일의 MIME 타입을 'text/plain' 또는 'image/jpeg' 등으로 설정하여 악성 코드가 실행되지 않도록 할 수 있다. 그러나 만약 웹 브라우저가 MIME 타입을 무시하고 데이터를 직접 조사 (MIME sniffing) 하여, 예를 들어 'text/html' 또는 'text/javascript'로 해석한다면, 웹 서버에서 원래 의도했던 보안 조치가 무용지물이 되어 버린다. 이로 인해 공격자가 업로드한 악성 스크립트가 실행될 가능성이 생긴다.

 

2. XXssProtectionHeaderWriter

  • XSS — 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악성 해커가 특정 웹사이트에 악성 스크립트를 삽입하여, 그 웹사이트를 방문하는 사용자의 브라우저에서 스크립트를 실행하는 기법을 말함.

    1. 해커는 취약한 웹사이트에 악성 스크립트를 삽입한다. (예: 댓글, 게시글 등 사용자 입력을 통해)
    2. 특정 사용자가 해당 웹사이트에 방문하면, 웹사이트는 악성 스크립트를 포함한 페이지를 제공한다.
    3. 사용자의 브라우저에서 악성 스크립트가 실행된다. (예: 세션 쿠키 탈취, 페이지 조작 등)

  • 일반적으로 브라우저에는 XSS공격을 방어하기 위한 필터링 기능이 내장되어 있음
  • 물론 해당 필터로 XSS공격을 완벽하게 방어하지는 못하지만 XSS 공격의 보호에 많은 도움이 됨

3. CacheControlHeadersWriter — 캐시를 사용하지 않도록 설정

4. XFrameOptionsHeaderWriter — clickjacking 공격 방어

  • 웹 사용자가 자신이 클릭하고 있다고 인지하는 것과 다른 어떤 것을 클릭하게 속이는 악의적인 기법
  • 보통 사용자의 인식 없이 실행될 수 있는 임베디드 코드나 스크립트의 형태

4. HstsHeaderWriter — HTTP 대신 HTTPS만을 사용하여 통신해야함을 브라우저에 알림 (HTTPS 설정 시 관련 헤더 추가됨)

 

BasicAuthenticationFilter

  • Basic 인증을 처리함
    • HTTPS 프로토콜에서만 제한적으로 사용해야 함 (보통은 사용하지 않음)
    • HTTP 요청 헤더에 username과 password를 Base64 인코딩하여 포함

      Basic 인증에서는 사용자 이름과 비밀번호를 Base64로 인코딩해서 전송한다. 그러나 Base64 인코딩은 단순히 데이터를 문자열로 변환하는 방법이며, 복호화가 매우 쉽다. 따라서 이것은 실질적으로 비밀번호를 평문으로 전송하는 것과 다르지 않다.

      하지만 간단한 API를 개발하거나 빠르게 프로토타이핑을 해야하는 경우에는 Basic 인증이 간단하고 빠르게 개발할 수 있는 방법이 될 수 있다.
      또한, 클라이언트와 서버 사이에 안전한 연결이 이미 확립되어 있는 경우(예를 들어, 내부 네트워크에서만 사용되는 서비스)에도 Basic 인증이 충분히 안전할 수 있다.

 

CSRFFilter

  • 사용자가 자신의 의지와 무관하게 공격자가 의도한 행위를 웹서비스에 요청하는 행위

  1. 사용자 로그인: 사용자가 정상적인 웹 사이트에 로그인하고 세션을 유지한다.
  2. 악의적인 링크 생성: 공격자는 사용자가 클릭하게 만들려는 악의적인 요청을 포함한 링크나 이미지를 생성한다.
  3. 사용자 유인: 공격자는 이 링크를 이메일, 포럼, 소셜 미디어 등을 통해 사용자에게 전달한다.
  4. 요청 실행: 사용자가 링크를 클릭하면, 사용자의 브라우저는 악의적인 요청을 웹 사이트에 전송합니다. 사용자가 해당 사이트에 로그인한 상태라면, 웹 사이트는 이 요청을 사용자의 의도한 요청으로 인식하고 처리한다.
  5. 결과: 공격자는 사용자의 권한으로 웹 사이트에서 특정 행동을 수행하게 만듭니다. 예를 들어, 비밀번호 변경, 계정 정보 수정, 금융 거래 등이 가능하다.

해결 방법으로는 다음의 방법이 존재한다.

 

1. referrer 검증

 

웹 브라우저는 HTTP 요청을 보낼 때 'Referrer'라는 헤더를 포함시키는데, 이 헤더는 사용자가 현재 요청을 보내는 페이지의 URL을 담고 있다.

예를 들어, 사용자가 'https://www.example.com/page1' 링크를 클릭해 'https://www.example.com/page2'로 이동한다면, 브라우저는 'page2'로의 요청에 'Referrer: https://www.example.com/page1' 헤더를 추가한다.

 

Referrer 검증은 웹 서버가 이 'Referrer' 헤더를 검사하여 요청이 같은 도메인에서 발생했는지 확인하는 것이다. CSRF 공격은 사용자가 공격자가 조작한 다른 사이트를 통해 요청을 보내는 것이기 때문에, 'Referrer' 헤더는 이를 판별하는 데 도움이 된다. 웹 서버는 'Referrer'가 자신의 도메인과 일치하지 않는 경우 요청을 거부하거나 무시할 수 있다

 

2. csrf토큰 활용

 

  • 사용자의 세션에 임의의 토큰값을 저장.
  • 리소스를 변경하는 요청(post. put, update, delete)와 같은 요청에는 토큰값을 포함해야함.
  • 사용자의 요청에 포함된 토큰값과 세션에 저장된 토큰값이 일치하는지 확인.
  • 일치하지 않으면 오류를 내보낸다.

 

WebAsyncManagerIntegrationFilter


Spring MVC Async Request (반환 타입이 Callable) 처리에서 SecurityContext를 공유할수 있게 함
Callable 실행 로직이 다른 쓰레드에서 실행되었음에도 SecurityContext를 제대로 참조 가능