Web - 토큰이란?

uk·2023년 1월 29일

토큰(Token)이란?

토큰은 세션 기반 인증의 한계를 극복하고자 고안되었다. 세션 기반 인증은 서버에서 유저의 상태를 관리하는데 사용자가 많아질수록 가용 메모리 부족과 부하가 발생한다.

우리가 현실에서 흔하게 사용하는 건물 출입증, 교통 카드를 떠올려보면 이해하기 쉽다.

많은 개발자들은 서버의 부담을 줄이기 위해 서버가 아닌 클라이언트에 인증 상태를 저장하는 방법을 고민하게 되었고 그 결과 토큰이 등장하였다. 토큰은 현재 가장 많이 사용되는 인증 방식 중 하나이다.

웹 보안에서 토큰은 사용자 인증 정보(Authentication)와 권한 정보(Authorization)를 포함한 정보를 담은 암호화된 문자열이다.


토큰 인증 방식의 흐름

  1. 클라이언트가 서버에 요청(로그인)을 보내면 서버는 DB에 저장된 사용자의 인증 정보(id, pw)를 확인한다.

  2. 인증 정보가 유효하다면 사용자의 인증 및 권한 정보를 서버의 비밀 키와 함께 토큰으로 암호화한다.

  3. 서버는 Authorization 헤더 및 쿠키를 통해 클라이언트에게 토큰을 전달한다. 쿠키는 크기 제한이 있어 대부분 Authorization 헤더를 사용한다.

  4. 클라이언트는 서버로부터 전달받은 토큰을 저장하는데 저장 위치는 브라우저의 로컬 스토리지, 세션 스토리지, 쿠키, 변수 등 다양하다.

  5. 토큰이 저장되면 클라이언트는 매 요청 시 Authorization 헤더나 쿠키를 통해 토큰을 전달한다. 쿠키는 크기 제한이 있어 대부분 Authorization 헤더를 사용한다.

  6. 서버는 전달받은 토큰을 비밀 키로 검증하고 토큰이 위조 되었는지, 유효 기간이 지났는지 확인하여 유효하다면 클라이언트에게 응답 데이터를 전송한다.


JWT(JSON Web Token)

토큰 기반 인증 구현 시 대표적으로 사용하는 기술로 JWT가 있다. JWT는 JSON 객체에 정보를 담고 이를 토큰으로 암호화하여 전송할 수 있는 기술이다. 클라이언트가 서버에 요청을 보낼 때 인증 정보를 암호화된 JWT 토큰으로 제공하고 서버는 이 토큰을 검증하여 인증 정보를 확인할 수 있다.

JWT의 구조

JWT는 . 으로 구분된 세가지 문자열로 나뉘어져 있으며 각각 Header, Payload, Signature 라고 부른다.

1. Header

Header는 해당 토큰 자체를 설명하는 데이터가 담겨 있다. 토큰의 종류, 시그니처를 만들 때 사용할 알고리즘을 JSON 형태로 작성한다.
아래 JSON 객체를 base64로 인코딩하면 header가 완성된다.

{
  "typ": "JWT",
  "alg": "HS256"
}
  1. typ : 토큰의 타입을 지정한다.
  2. alg : 해싱 알고리즘을 지정한다. 보통 SHA256, RSA가 사용되며 토큰을 검증할 때 사용되는 signature에서 사용된다.
  • base64 방식은 얼마든지 디코딩이 가능한 인코딩 방식이기 때문에 비밀번호와 같이 노출되면 안되는 민감한 정보를 담지 않아야 한다.

2. Payload

Payload는 전달하려는 내용물을 담고 있는 부분이다. 어떤 정보에 접근 가능한지에 대한 권한과 유저의 개인정보, 토큰의 발급 시간 및 만료 시간 등의 정보들을 JSON 형태로 담는다.

아래 JSON 객체를 base64로 인코딩하면 Payload가 완성된다.

{
  "sub": "test",
  "name": "admin",
  "iat": 168759912,
  "exp": 1916548905078
}

3. Signature

Signature는 토큰의 무결성을 확인할 수 있는 부분이다. Header와 Payload가 완성되었다면 Signature는 이를 서버의 비밀 키와 Header에서 지정한 알고리즘을 사용하여 해싱한다.

Payload 변조를 시도하더라도 토큰을 발급할 때 사용한 Secret을 정확하게 알고 있지 않은 경우 유효한 Signature를 만들어낼 수 없기 때문에 서버는 Signiture를 검증하는 단계에서 올바르지 않은 토큰임을 알아낼 수 있다.

HMAC SHA256 알고리즘을 사용한다면 Signature는 아래와 같은 방식으로 생성된다.

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

토큰의 장점

  1. 서버가 유저의 인증 상태를 관리하지 않고 비밀 키를 통해 클라이언트에서 보낸 토큰의 유효성만 검증하면 되기 때문에 무상태적인 아키텍처를 구축할 수 있다.

  2. 다수의 서버가 공통된 세션 데이터를 가질 필요가 없으므로 서버를 확장하기에 용이하다.

  3. 토큰의 생성과 검증이 하나의 서버에서 이루어지지 않아도 되기 때문에 토큰 생성만을 담당하는 서버를 구축할 수 있다. 이를 잘 활용하면 여러 서비스 간의 공통된 인증 서버를 구현할 수 있다.

  4. 토큰은 인증 상태와 접근 권한 등 다양한 정보를 담을 수 있기 때문에 사용자 권한 부여에 용이하다. 이를 활용해 어드민 권한 부여 및 정보에 접근할 수 있는 범위도 설정할 수 있다.


토큰의 단점

  1. Signature를 사용해서 위조된 토큰을 알아낼 수는 있지만 토큰 자체가 탈취될 위험이 있다.

  2. 인증 상태를 관리하는 주체가 서버가 아니므로 토큰이 탈취되어도 해당 토큰을 강제로 만료시킬 수 없다. 따라서 토큰이 만료될 때까지 사용자로 가장해 계속해서 요청을 보낼 수 있다.

  3. 토큰 탈취를 대비해서 유효 기간을 짧게 설정하면 사용자는 토큰이 만료될 때마다 다시 로그인을 진행해야 하기 때문에 좋지 않은 사용자 경험을 제공한다. 그렇다고 유효 기간을 길게 설정하면 토큰이 탈취될 경우 더 치명적으로 작용한다.

  4. 토큰에 여러 정보를 담을 수 있는 만큼 많은 데이터를 담으면 그만큼 암호화하는 과정도 길어지고 토큰의 크기도 커지기 때문에 네트워크 트래픽 문제가 발생할 수 있다.


액세스 토큰(Access Token)과 리프레시 토큰(Refresh Token)

토큰 인증 방식의 단점을 보완하기 위해 토큰의 유효 기간을 짧게 설정하거나 토큰의 유효 기간을 지속적으로 늘려주는(Sliding Session) 방법이 있지만 가장 대표적인 방법은 액세스 토큰리프레시 토큰을 사용하는 것이다.

액세스 토큰은 서버에 접근하기 위한 토큰으로 보안을 위해 1시간 미만의 짧은 유효 기간을 설정한다.

리프레시 토큰은 액세스 토큰이 만료되었을 때 새로운 액세스 토큰을 발급하기 위해 사용되는 토큰이다. 리프레시 토큰은 액세스 토큰보다 긴 1~2주의 유효 기간을 설정한다.

액세스 토큰이 만료될 경우 클라이언트는 리프레시 토큰을 통해 액세스 토큰 재발급을 요청하고 서버는 리프레시 토큰이 유효할 경우 새로운 액세스 토큰을 발급해 준다. 이로써 사용자는 다시 로그인을 할 필요 없이 지속해서 인증 상태를 유지할 수 있으며 리프레시 토큰마저 만료될 경우 새로 로그인을 해야 한다.


하지만..

리프레시 토큰도 모든 문제를 해결해 주진 않는다. 리프레시 토큰은 긴 유효 기간을 가지고 있어서 탈취될 경우 악의적인 유저가 계속해서 액세스 토큰을 생성하고 사용자의 정보를 해킹할 수도 있기 때문이다. 이를 대비하기 위해 리프레시 토큰을 세션처럼 서버에 저장하고 이에 대한 상태를 관리하기도 한다.

결국 서버에서 상태를 관리하지 않기 위해 고안된 토큰 인증도 보안성을 위해 일정 부분 서버에서 상태 관리를 담당하는 것처럼 결국 이 세상에 완벽한 보안은 없다. 세션, 토큰 등 다양한 보안 방식 및 여러 구현 방법들은 절대 뚫리지 않는 궁극의 보안을 위해 만들어진 것이 아니다. 이러한 여러 방식들은 단순히 보안뿐만 아니라 보안과 사용자 경험 사이의 적절한 균형을 찾기 위해 만들어졌고 개발자로서 구현하려는 서비스에 어떤 인증 방식이 가장 적절한지 판단할 줄 아는 것이 가장 중요하다.

0개의 댓글