[Web] 쿠키 / 세션 / 토큰 (JWT)

박성우·2023년 7월 26일
0

Web

목록 보기
2/3

유저 인증/인가 방식에 대표적인 키워드 쿠키, 세션과 토큰을 알아보고자 한다.

❗이 세 가지 인증 방식은 서로를 완전히 대체하기 위해 나온 방식들이 아니다. 예를 들어, 인증/인가에 쿠키만 사용할 수도 있고, 세션 & 쿠키, 토큰 & 쿠키 이런 식으로 조합되어서도 사용할 수 있다.

보안 문제로 인해 쿠키만으로 인증/인가를 해결하는 경우는 거의 없지만, OAuth를 제외하고 상황에 따라 세션 방식 vs 토큰 방식으로 나뉘는 경우가 많으며 토큰과 함께 사용되는 쿠키 또한 Local Storage, Session Storage 등 다른 저장소로 대체되는 경우도 많기 때문에 각각의 방식을 이해하는 것이 중요하다.


  • 서버로부터 전송받고 브라우저에 저장되는 key-value 형식의 데이터 조각
  • 쿠키 기반 인증은 브라우저에 유저 정보를 저장

애초에 쿠키가 필요하게 된 핵심적인 이유는 바로 HTTP 프로토콜의 무상태성(Stateless) 때문이다.

Stateless 하다는 것은 말 그대로 State(상태)가 없다는 뜻이며, HTTP 프로토콜에 의한 통신은 모든 요청이 독립적이고 어떤 유저가 요청을 보냈는지를 기억하고 있지 않기 때문에 State를 보존하지 않는다는 의미로 Stateless 하다고 말한다.

Stateless 의 장점은 서버에서 클라이언트의 상태를 보존하지 않기 때문에, 서버의 확장성에 매우 유리하다.

하지만, 로그인이 필요한 서비스에서는 요청이 올때마다 유저를 구분할 필요가 있는데, 유저의 상태를 보존하지 않는다면 매번 아이디와 비밀번호를 입력해야만 로그인이 가능하다.

이러한 불편함을 개선하기 위해 처음 로그인을 하면, 서버에서는 쿠키라는 데이터 조각에 유저의 정보를 저장해서 유저에게 전달해주고, 유저는 브라우저에 쿠키를 저장해두었다가 로그인이 필요할 때마다 계속 사용하게 되는 것이다.

또한, 그 외에도 장바구니, 아이디 비밀번호 자동 저장, 팝업 더 이상 보지않기 기능 등 유저의 기록을 저장하는 용도로도 유용하다.

즉, Stateless한 통신 사이에서 Stateful한 경우를 대처하기 위해 생긴 방식인 것이다.

하지만, 쿠키에 유저의 비밀번호와 같은 중요한 개인 정보를 담아서 인증/인가를 하게되면 쿠키가 탈취될 경우 유저의 개인 정보가 노출되게 된다는 큰 문제점이 존재한다.

물론, 쿠키의 보안을 강화할 수 있는 방법은 존재한다.

쿠키의 보안을 강화하는 방법에는 httpOnly 옵션을 두어 XSS 공격에 의해 삽입된 JS로 쿠키가 변조되는 것을 막거나, Secure 옵션을 두어 HTTPS 요청에만 쿠키가 전송되거나, SameSite 옵션을 통해 Cross Site로 전송되는 요청의 경우 쿠키 전송에 제한을 두는 등이 있지만, 여전히 브라우저에 유저의 개인 정보를 저장하는 것은 매우 위험하다.

이를 위해, 세션을 사용하는 방식이 등장하였다.

세션 (Session)

  • 클라이언트와 서버 간의 특정 기간 동안 지속되는 상태를 유지하는 기술
  • 세션 기반 인증은 유저 정보를 서버에 저장하고 세션 ID를 쿠키에 넣어서 유저에게 전달

세션 또한 의미 그대로 Stateless한 통신 사이에서 Stateful한 경우를 대처하기 위해 생긴 기술이고 결국은 쿠키에 세션 ID를 담기 때문에 쿠키 방식과 비슷하지만 유저 정보를 서버에서 보관하고 있기 때문에, 쿠키를 탈취당해도 쿠키에 유저의 개인 정보가 들어있지 않기 때문에 보안을 강화할 수 있다.

세션의 장점으로는 보안 강화 외에도 Stateful하게 유저의 로그인 상태를 모두 저장하고 있기 때문에, 세션을 삭제해서 강제로 로그아웃을 시키거나, 같은 아이디/비밀번호로 접속하는 인원 제한 등 유저의 상태를 관리하기 용이한 장점이 있다.

하지만, 서버에 저장한다는 것은 메모리가 필요한 것이며 결국 유저 정보가 늘어날수록 저장해야할 데이터가 많아지고, 유저가 세션 ID와 함께 인증을 요구할 때마다 많아지는 데이터 속에서 세션 ID와 일치하는 유저를 찾아야하는 과정이 동반되어 결국 서버에 부하가 커지는 큰 단점이 존재한다.

게다가 메모리는 휘발성이기 때문에 서버가 꺼지면 세션이 초기화되고 모든 유저는 재로그인을 해야하는 상황이 올 수 있다.

또한, HTTP 요청의 Stateless 함과 세션의 Stateful 한 패러다임이 충돌하여 서버가 단일 서버 이상으로 확장되었을 경우 세션 정보가 공유되지 않기 때문에 세션 불일치 문제가 발생한다.

위와 같은 문제들을 해결하기 위해서 Redis와 같은 In-Memory DB를 사용하여 공용 세션 DB를 만든다거나, Sticky Session을 통한 특정 세션 지정, Session Clustering을 통한 세션 복사와 같은 방안 등 여러 해결 방안들이 존재하지만 전부 상당한 비용과 오버헤드 등을 발생시키는 문제가 있으며, 모바일 디바이스와 같은 경우는 쿠키가 따로 존재하지 않기 때문에 세션을 이용하기에 불편한 부분이 있다.

이러한 문제를 해결하기 위해 나온 방식이 토큰 기반 방식이다.

JWT (Json Web Token)

  • Json 포맷을 이용하여 사용자에 대한 속성(개인 정보 X)을 저장하는 Claim 기반의 Web Token
  • 토큰 기반 인증은 서버에 유저 정보를 저장하지 않고 토큰(서버에서 발급하는 일종의 인코딩된 문자열)을 브라우저에 저장

JWT는 위와 같이 header, payload, signature로 구성되어있고 header에는 토큰의 타입과 암호화 알고리즘 방식, payload에는 유저의 이메일이나 닉네임과 같은 서비스가 공개하기 윈하는 정보나 유효기간 등이 존재하는데, header와 payload는 단순히 Base64를 통해 인코딩되어 있기 때문에 누구나 디코딩하여 정보들을 볼 수 있다.

signature는 header, payload와 서버에서 관리하는 Secret Key를 header에 명시한 알고리즘으로 암호화하고, signature 값과 일치하면 인가하게 되기 때문에, 토큰을 조작하는 것은 불가능하다. 누군가가 토큰을 탈취하고 payload 값을 변경하면 signature 값 또한 변경되기 때문이다.

결과적으로, 토큰은 따로 세션처럼 서버 내에 유저의 정보를 저장하고 상태를 보존하지 않기 때문에 Stateless 함을 유지할 수 있고, 서버는 유저가 브라우저에 저장해놓고 건네 받은 JWT만을 확인해서 인증을 하게 되기 때문에 따로 세션 DB와 같은 서버 저장소가 필요없는 장점이 존재한다.

하지만, JWT는 서버에서 한번 발급하고 나면 그 후로는 JWT를 관리하지 않고, 유저의 상태를 관리할 수 없기 때문에 토큰이 탈취당할 경우 인증을 허가할 수 밖에 없다.

이를 해결하기 위한 방법 중 대표적으로 토큰을 두 개로 나누는 방법이 존재하는데, Access Token과 Refresh Token이다.

짧은 유효 기간을 가진 Access Token을 발급하여 탈취되더라도 금방 만료되게 하고, Refresh Token을 같이 발급하여 Access Token이 만료될 때마다 유저의 Refresh Token과 서버에 저장된 Refresh Token을 대조하여 유효한 경우 새로운 Access Token을 발급하도록 하여 보안을 강화한 것이다.

또한, 로그아웃을 할 경우 Access Token과 Refresh Token을 삭제하고 따로 토큰에 대한 BlackList를 두어 강제 로그아웃 기능을 구현하거나 탈취를 어느정도 방지할 수 있다. (Redis를 이용한 로그아웃 구현)

🤔아이러니한 점은 Stateless를 유지하고 서버에 데이터를 저장하지 않기 위해 토큰 기반 인증을 도입했는데, JWT의 보안 문제로 인해 위 해결 방안은 결국 서버에 Refresh Token와 BlackList 저장을 위한 데이터베이스가 필요하게 되고 Stateful에 가까워진다는 점이 아닌가 싶다.

그래도 전체 세션 상태를 유지하는 것보단 훨씬 Stateless하고 매번 세션 ID를 통해 유저를 조회하는 것에 비해 토큰을 파싱하고 검증하기만 하면 되므로 여전히 장점으로 볼 수 있는 것 같다.

그 외에도, 토큰을 쿠키에 저장하고 처음에 설명한 쿠키의 보안 옵션 등을 통해 보안을 강화하는 방법도 존재한다.


토큰 기반 방식이 제일 나중에 나온 방식이며 세션에 비해 경제적이고 Stateless를 통해 서버의 확장성 면에서 비교적 우수하지만, 어느 정도 규모가 있는 서비스에서는 세션 방식을 사용하는 경우가 많다고 한다.

현재 로그인된 사용자의 모든 디바이스들을 나열해주거나, 특정 디바이스에서 로그아웃을 허용하는 기능 등 유저의 상태를 완전히 관리할 수 있는 세션을 사용하지 않고서는 어렵기 때문이다.

또한, 토큰은 여전히 보안 측면에서 세션에 비해서 부족한 것으로 보인다.

결국 제공하고자 하는 서비스 특성이나 규모에 따라서 적절한 인증/인가 방식을 채택하는 것이 중요하다.


Reference

https://www.youtube.com/watch?v=y0xMXlOAfss&t=740s
https://hudi.blog/session-based-auth-vs-token-based-auth/
http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
https://velog.io/@hyehyeonmoon/%EB%8F%99%EA%B8%80-JWT%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%97%90%EC%84%9C-%EB%B3%B4%EC%95%88%EC%9D%84-%EB%86%92%EC%9D%B4%EB%8A%94-%EB%B0%A9%EB%B2%95HTTPS-Cookie

profile
Backend Developer

1개의 댓글

comment-user-thumbnail
2023년 7월 26일

공감하며 읽었습니다. 좋은 글 감사드립니다.

답글 달기