Day5. Springboot : JWT 인증에서 쿠키 활용 방식 개선

2ㅣ2ㅣ·2024년 10월 31일

Project

목록 보기
4/13

개요

JWT 기반 인증에서 초기에는 쿠키를 사용해 프론트엔드에서 JWT를 처리했으나, HttpOnly 옵션 활성화로 인해 JavaScript에서 쿠키를 직접 읽을 수 없는 문제가 발생했다. 또한, 기존 인증 흐름 설계상, 쿠키를 활용하는 방식에서도 문제점이 발생했다. 이를 해결하기 위해, JWT를 브라우저의 JavaScript에서 직접 관리하지 않고, 서버에서 요청받은 쿠키를 읽어 처리하는 방식으로 변경했다.



Cookie + JWT 조합을 선택한 이유는 간단하다. 브라우저는 기본적으로 서버에 쿠키를 자동으로 전송한다. 이 기능을 활용해, 전송되는 쿠키에 인증 정보를 담아 서버와 주고받는 방식이 더 효율적이고 간결하기 때문이다.



As-Is

1. 기존 인증 흐름: 요청 헤더에서 JWT 추출

초기 인증 흐름에서는 JWT를 Authorization 헤더에서 직접 추출해 사용했다.

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    if (isWhiteListed(request.getRequestURI())) {
        filterChain.doFilter(request, response);
        return;
    }

    String authorizationHeader = request.getHeader("Authorization");
    if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
        String jwtToken = extractAccessToken(request);

        try {
            Authentication authentication = jwtProvider.getAuthentication(jwtToken); // JWT 검증
            SecurityContextHolder.getContext().setAuthentication(authentication);   // 인증 설정
        } catch (Exception e) {
            sendJwtExceptionResponse(response, new RuntimeException("인증 실패")); // 인증 실패 시 응답
            return;
        }
    }

    filterChain.doFilter(request, response); // 필터 체인 진행
}

문제점

  1. JWT를 Authorization 헤더에서 직접 읽어오기 때문에, 프론트엔드에서 헤더를 추가해야 하는 번거로움이 있었다.
  2. 헤더에 JWT를 추가하는 과정에서 XSS와 같은 보안 문제가 발생할 가능성이 있다.
  3. 쿠키를 활용하려 해도, 이 설계에서는 HttpOnly 옵션 여부와 무관하게 클라이언트가 JWT를 헤더에 수동으로 넣어야 해서 문제가 계속된다.



2. LoginFilter로 JWT 쿠키 생성

초기에는 JWT를 쿠키에 저장하고 HttpOnly 옵션을 비활성화하여 JavaScript가 쿠키를 읽고 사용할 수 있도록 했다.

public void addJwtToCookie(HttpServletResponse response, String jwtToken, String cookieName) {
    Cookie cookie = new Cookie(cookieName, jwtToken);
    cookie.setHttpOnly(false); // JavaScript에서 쿠키 접근 가능
    cookie.setSecure(false);  // HTTPS 필수 여부 미적용
    cookie.setPath("/");      // 쿠키 경로 전체 허용
    cookie.setMaxAge(60 * 120); // 쿠키 유효 시간 (2시간)
    response.addCookie(cookie);
}

문제점

  • XSS 공격 위험: JavaScript에서 쿠키를 읽을 수 있어 JWT가 노출될 수 있다.
  • 프론트엔드 의존: 프론트엔드에서 JWT를 헤더로 추가해야 인증이 가능하다.



To-Be: 개선된 쿠키 처리 방식

1. 개선된 인증 흐름: HttpOnly 쿠키 사용

JWT를 HttpOnly 쿠키에 저장하고, 서버가 요청받은 쿠키에서 값을 추출해 인증을 처리하도록 변경했다.

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
    if (isWhiteListed(request.getRequestURI())) {
        filterChain.doFilter(request, response); // 화이트리스트 요청은 인증 건너뛰기
        return;
    }

    String jwtToken = getCookieValueFromToken(request, "accessToken"); // 쿠키에서 JWT 추출

    try {
        Authentication authentication = jwtProvider.getAuthentication(jwtToken); // JWT 검증
        SecurityContextHolder.getContext().setAuthentication(authentication);   // 인증 객체 설정
    } catch (Exception e) {
        sendJwtExceptionResponse(response, new RuntimeException("인증 실패")); // 인증 실패 시 응답
        return;
    }

    String email = jwtProvider.getClaims(jwtToken).getSubject(); // JWT에서 사용자 이메일 추출
    request.setAttribute("email", email); // 요청 속성에 사용자 이메일 추가

    filterChain.doFilter(request, response); // 다음 필터로 진행
}



  • getCookieValueFromToken: 쿠키에서 JWT 추출
    요청의 쿠키 배열에서 특정 이름(accessToken)의 값을 찾아 반환한다.
private String getCookieValueFromToken(HttpServletRequest request, String cookieName) {
    Cookie[] cookies = request.getCookies();
    if (cookies == null) return null; // 쿠키가 없으면 null 반환
    for (Cookie cookie : cookies) {
        if (cookieName.equals(cookie.getName())) {
            return cookie.getValue(); // 지정된 이름의 쿠키 값 반환
        }
    }
    return null;
}
  • jwtProvider.getAuthentication: JWT를 검증하고 인증 객체를 생성한다.
  • sendJwtExceptionResponse: 인증 실패 시, HTTP 응답으로 오류 메시지를 반환한다.



결론

JWT 기반 인증에서, 브라우저가 자동으로 서버로 전송하는 쿠키에 인증 정보를 담아 주고받는 방식으로 설계했다.

  1. HttpOnly 옵션을 활성화하여, JavaScript에서 쿠키에 접근할 수 없도록 설정한다.
    • 이를 통해 XSS 공격으로 JWT가 노출되는 문제를 방지한다.
  2. 브라우저의 자동 쿠키 전송 기능을 활용하여, 클라이언트에서 쿠키를 수동으로 추가할 필요 없이 요청마다 인증 정보를 포함하도록 한다.
    • 브라우저가 쿠키를 자동으로 전송하므로 인증 흐름이 단순화된다.

이로써 보안 강화클라이언트 코드의 간결화를 동시에 충족할 수 있다.

0개의 댓글