쿠키, 세션, 토큰
기본적으로 HTTP통신은 무상태(stateless)이다.
무상태란 말 그대로 클라이언트와 서버의 연결된 상태를 유지하지 않는다. 때문에 한번의 요청과 한번의 응답이 끝나면 서로 다른 요청 간에는 어떠한 정보도 공유하지 않는다.
그렇다면 로그인과 같이 상태를 계속 유지하기 위해서는 어떻게 할까? 웹 서버는 클라이언트 인증을 위해 인식표를 나눠주어 사용자들을 구분한다.
서버는 각 사람들을 구분하기 위해 인식표를 나눠주고, 그 인식표에 대해서 정보를 관리하는 식으로 연결을 유지한다.
서버가 클라이언트 인증을 확인하는 방식에는 3가지(쿠키, 세션, 토큰)가 있으며 서버는 사용자 자체를 인식하는 것이 아니라 사용자가 가진 인식표를 인식하는 것이고, 이 인식표는 언제든지 탈취될 가능성이 있기 때문에 아주 세심하게 관리해야 한다.
인식표를 어떻게 관리하는지 알아보기 전에 서버가 클라이언트 인증을 확인하는 방식에 대해 알아보았다.
쿠키(Cookie)인증
1. 쿠키(Cookie)란?
웹브라우저에서 쿠키(Cookie)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각으로,
key=value 형식의 문자열 데이터 묶음이다.
브라우저는 이 문자열 데이터 조각들을 저장해놓았다가 동일한 서버에 재요청시 쿠키 데이터를 전송할 수 있다.
즉, HTTP 프로토콜은 기본적으로 무상태성을 가지고 있기 때문에
서버와 클라이언트 간의 연결 유지를 구현하기 위해 서로를 인식할 수 있는 식별데이터가 필요했다.
그것이 쿠키 데이터 조각이라고 생각하면 된다.
2. 쿠키 인증 방식
브라우저(클라이언트)가 서버에 요청을 보낸다.
서버는 클라이언트의 요청에 대한 응답을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더의 Set-Cookie에 담는다.
이후 해당 클라이언트는 요청을 보낼 때마다, 매번 저장된 쿠키를 요청 헤더의 Cookie에 담아 보낸다.
3. 쿠키 인증방식의 단점
가장 큰 단점은 보안에 취약하다.
요청시 쿠키의 값을 그대로 보내기 때문에 유출, 조작 당할 위험존재쿠키에는용량 제한이 있어 많은 정보를 담을 수 없다.
웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저간 공유 불가능
쿠키의 사이즈가 커질수록 네트워크에 부하가 심해진다.
세션(Session)인증
1. 세션(Session) 인증이란?
위에서 보았던 쿠키 인증방식은 브라우저의 로그인 상태를 유지할수는 있지만, 보안의 위험이 있었다.
이를 해결하기 위해서 세션은 비밀번호, 카드번호 등의 중요한 정보는 서버측에서 관리하는 방식이다.
클라이언트가 서버의 요청을 보낼 때, 서버는 저장해둔 세션Id를 활용한다.
로그인상황을 예로들면, 로그인 요청이 성공하면 서버는 세션Id를 서버에 저장해두고,
클라이언트는 브라우저의 세션Id를 저장해둔다.
이제 서버는 다음 요청이 들어올 때마다, 쿠키의 세션 Id의 유효성 검사를 하는 것이다.
2. 세션 인증 방식
사용자가 로그인을 요청
서버에서 계정 정보를 읽어 사용자를 확인, 사용자의 고유한 Id를 부여하여
- 세션 저장소에 저장한후, 이와 연결된 세션Id를 발급한다.
사용자는 서버에서 해당 세션Id를 받아 쿠키에 저장한 후,
- 인증이 필요한 요청마다 쿠키를 헤더에 실어 보낸다.
서버는 쿠키를 받아 세션 저장소에서 대조 후 대응되는 정보를 가져온다.
인증이 완료되면 서버는 사용자에 맞는 데이터를 보내준다.
3. 세션 인증방식의 단점
쿠키를 포함한 요청이 외부에 노출되더라도 세션Id 자체에는 유의미한 개인정보를 담고있지 않다. 그러나, 탈취자가 세션Id 자체를 탈취하여 클라이언트인척 위장할 수 있다는 한계가 있다.
서버에서 세션 저장소를 사용하므로 요청이 많아지면 서버에 부하가 심해진다.
토큰(Token) 인증
1. 토큰(Token) 인증이란?
토큰 인증은 클라이언트가 서버에 접속을 하면 서버에서 해당 클라이언트에게 인증되었다는 의미로 ‘토큰’을 부여한다.
이 토큰은 유일하며, 토큰을 발급받은 클라이언트는 또 다시 서버에 요청을 보낼 때 요청 헤더에 토큰을 심어서 보낸다.
그러면 서버에서는 클라이언트로부터 받은 토큰을 서버에서 제공한 토큰과의 일치 여부를 체크하여 인증과정을 처리한다.
토큰은 서버가 아닌 클라이언트에 저장되기 때문에 메모리나 스토리지 등을 통해 세션을 관리했던 서버의 부담을 덜 수 있다.
토큰 자체에 데이터가 들어있기 때문에 클라이언트에서 받아 위조되었는지 판별만 하면 되기 때문이다.
2. 토큰 인증 방식
사용자가 아이디와 비밀번호로 로그인을 한다.
서버측에서 사용자에게 유일한 토큰을 발급한다.
클라이언트는 서버측에서 전달받은 토큰을 쿠키나 스토리지에 저장해두고,
- 서버에 요청을 할 때마다 해당 토큰을 HTTP요청 헤더에 포함시켜 전달한다.
서버는 전달받은 토큰을 검증하고 요청에 응답한다.
- 토큰에는 요청한 사람의 정보가 담겨있어서 서버는 DB를 조회하지않고 누가 요청했는지 알 수 있다.
3.토큰 인증방식의 단점
쿠키 / 세션과 다르게 토큰 자체의 데이터 길이가 길어서, 인증 요청이 많아질수록 네트워크 부하가 심해질 수 있다.
Payload 자체는 조회가 가능하기 때문에 유저의 중요한 정보를 담을 수 없다.
토큰은 발급하면 만료될 때까지 계속 사용이 가능하기 때문에 토큰이 탈취당하면 대처하기가 어렵다.
4. 토큰 유형- JWT (JSON Web Token)
JWT (JSON Web Token)
JWT (JSON Web Token)은 인증에 필요한 정보들을 암호화시킨 JSON 토큰을 의미한다.
세션 / 쿠키와 함께 가장 대표적인 인증 수단으로 JWT 토큰을 HTTP 헤더에 실어 서버가 클라이언트를 식별하는 방식이다.
JWT를 이용한 인증 과정
1. Access Token
사용자가 로그인을 한다.
서버에서는 계정 정보를 읽어 사용자를 확인 후, 사용자 고유ID값을 부여한 후, 기타 정보와 함께 Payload에 넣는다.
JWT의 유효기간 설정
암호화할 SECRET KEY를 이용하여 Access Token을 발급 한다.
사용자는 Access Token을 받아 로컬 스토리지(혹은 쿠키)에 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 실어서 보낸다.
서버에서는 해당 토큰의 Verify Signature을 SECRET KEY로 복호화한 후, 조작여부, 유효기간을 확인한다.
검증이 완료되면, Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져온다.
❗ 문제점
JWT 토큰은 탈취 당했을 때 문제가 발생한다. 세션 / 쿠키의 경우 만일 쿠키가 악의적으로 이용되고 있다면, 해당하는 세션을 지워버리면 된다.
하지만 앞서 말했듯이, JWT는 한번 발급되면 유효기간이 완료될 때까지 계속 사용이 가능하기 때문에 악의적인 사용자는 유효기간이 지나기 전까지 정보를 털어갈 수 있다.
심지어 본 주인인 클라이언트와 탈취한 사람을 서버는 구분할 수 없다. 따라서 유효 기간을 두어야하는 것이다.
💡 해결 방법
유효기간을 짧게 두면 사용자가 로그인을 자주 해야하므로 사용자 경험적으로 좋지 않고, 유효기간을 길게 두면 보안상 탈취 위험에서 벗어날 수 없다.
유효기간이 다른 JWT 토큰 2개(Access Token과 Refresh Token)을 두는 것이다.
기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급한다.
- Access Token의 유효기간은 짧다. (ex. 60일(마이크로소프트), 1시간(아마존))
- Refresh Token의 유효기간은 길다. (ex. 1년 (마이크로소프트))
즉, 통신과정에서 탈취당할 위험이 큰 Access Token의 만료 기간을 짧게 두고 Refresh Token으로 주기적으로 재발급함으로써 피해을 최소화한 것이다.
2. Refresh Token
사용자가 ID, PW를 통해 로그인
서버에서는 회원 DB에서 값을 비교한다. (보통 PW는 암호화해서 들어간다.)
사용자 인증이 되면 서버에서 Access Token, Refresh Token을 발급, 보통 회원 DB에 Refresh Token을 저장
서버는 사용자에게 Access Token, Refresh Token을 보낸다.
사용자는 Refresh Token을 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보낸다.
서버는 Access Token을 검증 후
이에 맞는 데이터를 사용자에게 보내준다.
시간이 흘러 Access Token이 만료
사용자는 만료된 Access Token을 헤더에 실어 요청을 보낸다.
서버는 Access Token이 만료됐음을 확인
만료된 토큰임을 알리고 권한없음을 신호로 보낸다. Access Token이 만료될때 마다 9~11 과정을 거칠 필요는 없다. Access Token의 Payload를 통해 유효기간을 알 수 있다.
- 따라서 프론트엔드 단에서 API 요청전에 토큰이 만료 됐다면 바로 재발급 요청 가능
사용자는 Refresh Token 과 Access Token을 함께 서버로 보낸다.
서버는 받은 Access Token이 조작되지 않았는지 확인하고, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교한다.
서버는 Refresh Token이 동일하고 유효기간도 지나지 않았다면 Access Token을 사용자에게 보내준다.
새로운 Access Token을 헤더에 실어 API 요청을 한다.
📑 참고 자료