본문 바로가기

프로젝트/중고 거래 플랫폼

[JWT]토큰별 Header와 Cookie를 동시에 사용한 이유

목차

     

    이번 프로젝트에서 Spring Security와 JWT를 사용한 토큰 방식 인가 처리를 구현하였다.

    JWT의 Access-Token은 HTTP Header에, Refresh-Token은 HttpOnly Cookie로 설정했었다.

    저번 프로젝트 회고 당시 '왜 이렇게 썼나요?' 라는 질문이 들어와 구현했던 과정의 이유에 대해 정리하고자 한다.

    Access Token in Header

    HTTP 헤더를 통한 AT 전송은 API 호출의 표준 방식이다.

    이 방식은 CSRF 공격에 자연스럽게 내성을 가지며, 다양한 클라이언트(웹, 모바일, 데스크톱 애플리케이션 등)에서 일관되게 사용할 수 있다. 구체적으로는 다음과 같은 장점을 가진다.

     

    장점

    1. 플랫폼 독립성

    액세스 토큰을 Header에 담으면 다양한 클라이언트 (웹, 모바일, 서버 간 통신 등)에서 사용하기 쉽다. 쿠키는 주로 웹에 최적화되어 있으므로 이러한 유연성이 떨어진다.


    2. Restful API 호환성

    REST 아키텍처에서는 상태 정보를 클라이언트가 관리하는 것을 원칙으로 하기 때문에 Header에 담는 방식이 적합하다.

     

    3. 명시성

    헤더에 'Authorization: Bearer {token}'과 같은 형태로 토큰을 전달하므로 어떤 데이터가 인증 데이터인지 명확하다.

     

    Access Token in HttpOnly Cookie?

    Access Token도 Cookie에 담으면 안 될까? 라는 궁금증에 찾아봤던 방법.

    결론적으로 말하자면 가능은 한데, 굳이? 라는 생각이다.

     

    AT도 HttpOnly 쿠키에 저장하는 방식을 고려할 수 있다.

    쿠키 방식을 사용하는 이유인 XSS 공격으로부터 추가적인 보호를 제공하며, CSRF 공격에 대해서는 적절한 방어 메커니즘을 사용하면 대응할 수 있을 것이다.

     

    하지만, 이 방식은 헤더를 통한 AT 전송의 유연성을 제한할 수 있으며, 특히 API 중심의 서비스에서는 적합하지 않을 수 있다. 또한, 다양한 클라이언트 환경에서의 호환성 문제를 고려해야 하는 단점 역시 존재한다.

    굳이 토큰 방식을 쿠키로 변경할 명확한 이유를 찾지 못했다.

     

    그에 반해 API 중심 애플리케이션에서 헤더 기반 AT가 적합한 이유는 다음과 같다.

     

    1. 플랫폼 독립성

    이전과 같은 장점이 사실 크다고 할 수 있다. 크로스플랫폼의 지원의 장점을 무시할 수 없다.

    하지만 우리 프로젝트의 경우 웹을 타깃으로 삼았기에, RT의 경우 쿠키 방식을 고려하였다.

     

    2. 보안과 통제 

    헤더를 통한 토큰 전송은 CSRF 공격에 대한 내성을 자연스럽게 제공한다. (토큰의 경우 클라이언트 주체가 직접 보내야 하고, 별다른 요청 없이 알아서 보내지는 쿠키의 방식과 다르기 때문이다.)

    또한, 클라이언트에서 HTTP 헤더를 통해 보내는 토큰의 사용을 보다 세밀하게 제어할 수 있다.

     

    3. CORS (Cross-Origin Resource Sharing)

    API를 다른 도메인에서 사용하는 경우, CORS 정책과 함께 헤더 기반 인증은 더욱 효과적으로 작동한다.

     

    Refresh Token in HttpOnly Cookie

    그렇다면 왜 이번 프로젝트에서는 RT를 Cookie 방식으로 사용했는지에 대해 이야기해보고자 한다.

    기존처럼 토큰 방식을 이용할 수도 있었지만, 크게 세 가지 장점을 차용하여 쿠키 방식을 도입하였다.

    장점

    1. XSS 방지

    HttpOnly 쿠키는 JavaScript를 통한 접근이 불가능하기 때문에, XSS 공격으로부터 보호된다.

    이는 토큰의 경우 클라이언트 웹에서 악성 스크립트로 탈취를 당할 가능성이 존재하지만, 이를 일차적으로 막아주는 보안의 역할을 한다.

     

    2. 자동 관리

    쿠키의 경우 클라이언트에서 특정 요청을 확인하고 보낼 필요 없이, 매 요청마다 자동으로 보내지기에 관리가 편하다는 장점이 있다. 또한 이는 클라이언트 측에서 별도의 인증 토큰 관리가 필요 없다는 것을 의미한다.

     

    3. 로직의 간편화

    우리 프로젝트에 해당하는 가장 핵심 이유이다.

    기존 토큰 방식의 경우 요청의 흐름은 다음과 같았다.

     

    AT 기반으로 일단 요청을 보내고, 이게 만료되었을 경우 재발급을 시행한다.

     

    다음은 흐름에 따른 서버의 요청 다이어그램이다.

    AT 토큰이 만료되었을 경우 RT를 통해 AT를 재발급 받아야 한다.

    이 과정에서 여러번의 요청이 발생하게 된다.

     

    하지만 RT를 쿠키 방식으로 하여 이러한 네트워크 오버헤드를 감소시킬 수 있다!

     

     

     

    단점 : CSRF 공격 위험성

    쿠키 기반 RT는 CSRF 공격에 노출될 수 있다.

    이는 명확한 단점으로, 공격을 방지하기 위해 적절한 CSRF 보호 메커니즘을 구현해야 한다.

     

    쿠키 기반의 Refresh Token (RT)이 CSRF 공격에 노출될 수 있는 이유는 쿠키는 자동으로 요청에 담겨진다는 특성 때문이다.

     

    자동 전송 특성

    브라우저는 쿠키를 해당 도메인의 모든 요청에 자동으로 첨부한다.

    공격자가 희생자를 속여 자신의 웹사이트로 유도하고, 희생자의 브라우저를 통해 악의적인 요청을 보낼 경우, 브라우저는 자동으로 RT를 포함한 쿠키를 전송할 것이다.

     

    그렇기에 HttpOnly 속성을 사용한다면 쿠키를 JavaScript로부터 보호하여 XSS 공격을 방지하지만, CSRF 공격을 막지는 못한다.

    CSRF 대응 방안

     

    AT (Access Token)를 헤더에 담고, RT (Refresh Token)를 HttpOnly 쿠키에 저장할 때 CSRF (Cross-Site Request Forgery) 공격을 방지하기 위한 대처 방안을 고려해야 한다.

    1. CSRF 토큰 사용

    클라이언트에 CSRF 토큰을 제공하고, 클라이언트는 모든 상태 변경 요청(POST, PUT, DELETE 등)과 함께 이 토큰을 서버에 전송한다.

    서버는 요청을 받을 때마다 이 CSRF 토큰을 검증하여, 요청이 신뢰할 수 있는 출처에서 왔는지 확인하는 방식이다.

     

    2. SameSite 쿠키 속성 설정

     

    쿠키에 SameSite 속성을 설정하여, 쿠키가 다른 사이트의 요청에 의해 전송되는 것을 제한한다.

    SameSite=Strict는 모든 크로스 사이트 요청에서 쿠키를 보내지 않게 하고, SameSite=Lax는 덜 엄격하며 GET 요청과 같은 일부 안전한 경우에만 쿠키를 허용합니다.

     

    결론

    AT를 헤더에, RT를 HttpOnly 쿠키에 저장하는 것은 보안과 유연성을 모두 고려한 좋은 접근 방식이라고 생각했다.

    어떤 방식을 선택하든, 적절한 보안 메커니즘(예: CSRF 보호, XSS 방지)을 구현하는 것이 중요하다!