HTTP
는 인터넷 상에서 데이터를 주고 받기 위한 서버/클라이언트 모델을 따르는 프로토콜
이다.
클라이언트가 서버에게 요청을 보내면, 서버는 응답을 보냄으로써 데이터를 교환한다.
HTTP
는 비연결성 및 무상태성이라는 특징을 가지고 있다.
HTTP
는 요청 처리 후 연결을 끊어버리기 때문에, 클라이언트의 상태 정보 및 현재 통신 상태가 남아있지 않다.
이 비연결성의 장점은 서버의 자원 낭비를 줄일 수 있다는 것이다.
만약 다수의 클라이언트와 연결을 유지한다면 자원 낭비가 심해질 것이다.
허나 비연결성은 클라이언트를 식별할 수 없다는 단점 또한 존재한다.
로그인을 하더라도 다음 요청에서 해당 클라이언트를 기억하지 못해서, 무한 로그인을 해야 할 것이다.
심지어 브라우저 새로고침을 누를 때마다 로그인을 해야한다….
이와 같은 문제점을 해결하기 위해 Cookie
와 Session
이라는 기술을 활용한다.
쿠키(Cookie)
란 클라이언트가 어떠한 웹사이트를 방문할 경우, 그 사이트가 사용되고 있는 서버를 통해 클라이언트의 브라우저에 설치되는 작은 기록 파일을 일컫는다.
서버는 클라이언트의 로그인 요청
에 대한 응답
을 작성할 때, 클라이언트 측에 저장하고 싶은 정보를 응답 헤더
의 Set-Cookie
에 담는다. (쿠키는 Key-Value
형식의 문자열이다.) 이후 해당 클라이언트는 요청을 보낼 때마다 매번 저장된 쿠키
를 요청 헤더의 Cookie
에 담아 보낸다. 서버는 쿠키
에 담긴 정보를 바탕으로 해당 요청의 클라이언트가 누군지 식별할 수 있다.
세션은 비밀번호 등 클라이언트의 인증 정보를 쿠키
가 아닌 서버 측에 저장하고 관리한다.
서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 때, 인증 정보는 서버에 저장하고, 클라이언트 식별자인 JSESSIONID
를 쿠키에 담는다. 이후 클라이언트는 요청을 보낼 때마다 JSESSIONID
쿠키를 함께 보낸다. 그리하면 서버는 JSESSIONID
의 유효성을 판별해 클라이언트를 식별한다.
JWT(JSON Web Token)
는 인증에 필요한 정보들을 암호화시킨 토큰이다.
JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT 토큰(Access Token)
을 HTTP 헤더
에 실어 서버가 클라를 식별한다.
JWT의 구조는 위 사진과 같이 세가지 문자열의 조합이다. 실제 디코딩된 JWT는 Header
, Payload
, Signature
로 이루어져 있다.
Header
는 alg
와 typ
을 갖고 있다.
alg
는 정보를 암호화할 해싱 알고리즘을, typ
는 토큰의 타입을 지정한다.
{
"alg": "HS256",
"typ": "JWT"
}
Payload
는 실제로 토큰에 담을 정보를 지니고 있다.
주로 클라이언트 고유 ID
, 유효 기간
등이 포함된다.
Key-Value
형식으로 이루어진 한 쌍의 정보를 Claim
이라고 한다.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516230922
}
Signature
는 인코딩된 Header
와 Payload
를 더한 뒤, 비밀키로 해싱하여 생성한다.
Header
및 Payload
는 단순 인코딩된 값이기 때문에 해커가 복호화하고 조작할 수 있지만, Signature
는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다.
따라서 Signature
는 토큰의 위변조 여부를 확인하는 데 사용된다.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret_key
)
Payload
에 담는다.Access Token(JWT)
을 발급한다.Authorization
에 포함시켜 함께 전달한다.Signature
을 비밀키로 복호화한 다음, 위변조 여부
및 유효 기간
등을 확인한다.Header
와 Payload
를 가지고 Signature
를 생성하므로 데이터 위변조를 막을 수 있다.토큰에 대한 기본 정보
와 전달할 정보
및 토큰이 검증됐음을 증명하는 서명
등 필요한 모든 정보를 자체적으로 지니고 있다.접근
및 권한 공유
가 가능하다.OAuth
의 경우 Facebook, Google 등 소셜 계정을 이용해 다른 웹서비스에서도 로그인 할 수 있다.쿠키
, 세션
과 다르게 JWT는 토큰의 길이가 길어, 인증 요청이 많을수록 네트워크 부하가 심해진다.Payload
자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다. (패스워드 등)이러한 단점을 보완하기 위한 토큰 전략들은 몇가지가 있다.
아래에서 살펴보자.
토큰의 만료 기한을 짧게 설정해서 탈취되더라도 빠르게 만료시키는 방법이다.
하지만 이는 토큰이 만료되면 사용자가 다시 로그인해야 한다는 뜻이기에 사용자 입장에서 번거로운 방법이다.
서비스를 지속적으로 이용하는 클라이언트에게 자동으로 토큰 만료 기한을 늘려주는 방법이다
만약 글을 작성하다가 토큰이 만료된다면 새로운 토큰을 발급해주는 것이다.
사용자가 로그인을 자주 할 필요가 없다.
클라이언트가 로그인할 때 Access Token
및 Refresh Token
을 발급해주는 방법이다.
Refresh Token
은 Access Token
보다 만료 기한이 긴 토큰이다.
클라이언트가 요청을 보냈는데 Access Token
이 만료되었을 때, Refresh Token
을 이용하여 Access Token
의 재발급을 요청한다.
이때 서버는 DB에 저장된 Refresh Token
과 비교하여 유효하면 Access Token
을 발급한다.
만약 Refresh Token
도 만료된 경우라면 사용자에게 로그인을 요구한다.
이 전략을 사용하면 Access Token의 만료 기한을 짧게 설정하여 위의 짧은 만료 기한 설정
전략처럼 탈취되더라도 빠르게 만료될 수 있다. 또한 짧은 만료 기한에도 불구하고 자주 로그인을 할 필요가 없어진다. 서버가 강제로 Refresh Token을 만료시킬 수도 있다.
하지만 이렇게 완벽하게 보이는 Refresh Token 발급 방법도 단점은 있다. Refresh Token 검증을 위해 DB(혹은 별도의 저장소)에 저장해야 하고, 자원이 소요될 뿐더러 추가적인 I/O 작업이 발생한다. (JWT의 장점은 I/O 작업이 필요없는 빠른 인증 처리였다.)
최근에는 서비스 개발에서 JWT를 가장 많이 사용하는 추세라고 한다.
서로의 장단점을 고려해서 내 서비스에 맞는 인증 방법을 사용하자!
긴 글 읽어주셔서 감사합니다 😀🙏
피드백은 언제나 환영입니다!!
https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/
https://jwt.io/
https://velog.io/@kingth/서버-인증-방식세션쿠키-토큰
좋은 글 잘보았습니다 ㅎㅎ
참고로 위에 설명하신refresh토큰관련 로직에 부연설명을 하자면
재발급토큰이 공격자에게 탈취될 경우, 해당 공격자가, 토큰을 계속 refresh하여 사실상 기한제한 없이 로그인을 유지할 수 있다는 단점이있어요.
따라서 이를 막기위해, refresh토큰의 payload를 캐시서버 또는 세션 스토리지에 저장한 후
refresh 요청시 서버측에 저장된 refresh토큰의 payload와 일치여부를 검사하는 방식이에요.
이를 통해서, 사용자가 로그아웃하거나 공격자에게 토큰을 탈취당해, refresh를 막아야 할 경우
서버측에 저장된 refresh 토큰의 payload를 제거하여 추후 refresh 요청시 검사를 실패하도록 방식이에요.
세션과는 다르게, redis에 저장된 payload만으로는 tokenizing 할 수 없어서 redis server 데이터가 유출되더라도, 클라이언트로의 위장이 불가능해요. 다만 세션과 마찬가지로 부하가 걸릴 가능성은 존재해요.
👍👍👍