Cookie vs Token authentication (번역)
클라이언트와 서버간의 통신을 안전하게 만들기 위해 리퀘스트 요청에 신원 확인을 위한 증명서를 결합합니다. 우리는 이것을 인증(authentication)이라 하며 유저의 신원 확인을 위해 유저 이름과 비밀번호와 같은 증명에 필요한 정보들을 비교하여 사용됩니다. 이 글에서는 쿠키와 토큰 베이스의 인증을 비교해보고 어떻게 사용되는지 비교해보겠습니다.
Prerequisites
이 글은 HTTP protocol을 이해하고 있다는 전제하에 쓰여졌습니다. 웹에서의 보안 이슈나 클라이언트-서버간의 흐름을 이해하고 있다면 이 글을 이해하는데 더욱 도움이 될겁니다.
Understanding cookie-based authentication
쿠키는 여러분이 웹사이트에 접속할때 서버가 만들어서 브라우저로 보내는 작은 데이터 조각입니다. 브라우저는 쿠키를 저장하고 있다가 같은 웹사이트에 다시 접속하게되면 서버로 돌려보냅니다. 서버에 같은 브라우저에서 요청이 왔다는것을 알리고 유저 인증을 유지하기 위해서 말이죠. 브라우저의 쿠키들은 “키와 값”의 한 쌍으로 읽습니다.
쿠키 베이스 인증은 클라이언트의 리퀘스트를 인증하고 무상태 HTTP 프로토콜에서 서버의 세션 정보를 유지하기 위해 HTTP 쿠키를 사용합니다.
쿠키 베이스 인증의 과정을 설명하자면 이렇습니다:
-
클라이언트에서 백엔드 서버에 증명서(credentials)와 함께 로그인 요청을 보냅니다.
-
서버는 증명서가 유효한지 검사합니다. 만약 로그인이 성공적으로 되었다면 웹 서버는 DB에 세션을 생성하고 Set-Cookie 헤더를 고유의 ID를 가지고 있는 쿠키 객체형태로 응답에 포함시켜 보냅니다.
-
브라우저는 쿠키를 로컬에 저장해놓습니다. 사용자가 계속해서 로그인 상태를 유지하는 동안 클라이언트는 매 리퀘스트마다 서버에 쿠키를 보냅니다. 그리고 서버는 쿠키에 저장이 되어있는 세션ID와 DB에 저장되어 있는 세션을 비교하고 유효성을 검증합니다.
-
로그아웃을 하게되면 서버는 쿠키를 DB에서 지움으로써 만료시킵니다.
Advantages of cookie-based authentication
-
쿠키 베이스의 인증을 사용하면 어플리케이션의 상태를 계속 유지시킬 수 있습니다(stateful). 이 장점은 유저의 상태를 추적하고 개인화 하는것에 효율적입니다.
-
쿠키의 사이즈는 굉장히 작으므로 클라이언트 사이드에서 저장하기에 효율적입니다.
-
쿠키는 “HTTP-only”이므로 클라이언트 사이드에 읽기란 불가능합니다. 이 장점은 어떠한 Cross-site scripting (XSS) 공격에도 더 나은 보안성을 제공합니다.
-
쿠키는 요청에 자동적으로 추가됩니다. 그러므로 개발자들은 직접 쿠키를 추가하지 않아도 됩니다.
Limitations of cookie-based authentication
-
쿠키 베이스 인증은 CSRF(Cross-site Request Forgery)공격에 취약합니다. 그래서 CSRF 토큰과 같은 다른 보안 수단이 더 필요합니다.
-
세션 데이터들을 DB나 서버 메모리에 저장해야합니다. 그러기에 사용자들이 많은 서비스에는 적합하지 않습니다.
-
쿠키는 하나의 도메인에서만 작동합니다. 예를들어 jabs.com에서 받은 쿠키를 boo.com 도메인에서는 읽거나 보낼수는 없는거죠. 그래서 모바일과 웹 플랫폼에서 다른 도메인을 사용하여 API 서비스를 이용할때 문제가 됩니다.
-
클라이언트에서 모든 요청마다 쿠키를 서버에 보내야합니다. 심지어 접속할때 인증이 필요없는 URL들에서두요.
Understanding token-based authentication
토큰 베이스 인증에서는 유저의 상태(state)를 클라이언트에 저장합니다. JSON Web Token (JWT)은 JSON 객체 형식으 클라이언트와 서버간의 안전한 데이터 전송 방법을 정의한 표준규격입니다. 저는 토큰과 JWT 용어를 번갈아 사용하면서 설명해보겠습니다.
jwt.io 웹사이트는 JWT 토큰 정보를 파싱할 수 있게 해줍니다. jwt.io에서 JSON Web Token을 디코딩, 인코딩을 하며 실험할 수 있습니다.
JWT 토큰을 해부해보면 세가지의 점(.)으로 구분되어 있는것을 보실 수 있습니다. 세가지 파트는 JWT 헤더, JWT 페이로드 그리고 시그니처(signature)로 각각 나뉠 수 있습니다.
JWT 토큰의 예시
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT 헤더는 Base64 URL-encoded JSON 객체입니다. 이 부분은 토큰의 타입과 HMAC, SHA256, or RSA와 같이 사용된 사인 알고리즘의 정보를 담고있습니다.
예시:
{
"alg": "HS256",
"typ": "JWT"
}
JWT 페이로드는 권한(claims)를 가지고 있는데요. 이 권한은 어떤 주제와 관련된 정보의 조각들을 이라고 보시면 됩니다. 이 권한은 유저의 관한 상태와 다른 기타 데이터를 가지고있습니다. 그리고 JWT안에 JSON Web Signature의 페이로드로 사용되어질 JSON 객체로서 인코딩이 되어있으며 공개적으로 혹은 비공개적으로 등록이 되있습니다.
예시:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
JWT signature를 만드는것은 인코딩된 헤더, 인코딩된 페이로드 그리고 시크릿 키, 특정 알고리즘 적용을 수반합니다.
예시:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
위의 예시는 HMAC SHA256 알고리즘을 사용하여 JWT 토큰을 생성했습니다. 이 알고리즘이 바로 토큰이 백엔드에 시크릿 키가 적용되었을때 변하지 않았다고 증명하는 시그니처(signature) 입니다.
토큰을 이용한 인증방식은 상태가 없는(stateless) 메커니즘으로서 서버 메모리나 데이터베이스에 유저 관련 정보가 절대 저장이 되지 않습니다. 그러므로 API와 downstream services를 여러 도메인에서 사용해도 아무런 이슈가 없습니다.
웹 어플리케이션에서 토큰을 이용한 인증은 이렇게 진행됩니다:
-
유저가 백엔드 서버에게 로그인 증명서를 보냅니다.
-
요청에 따라 서버에서는 증명서를 확인하고 시크릿 키와 함께 암호화된 JWT토큰을 생성 후 클라이언트에게 보내줍니다.
-
클라이언트에서 브라우저는 로컬 스토리지나 세션 스토리지 아니면 쿠키 스토리지에 토큰을 저장합니다.
-
다음에 서버에 요청을 할 때, JWT는 brearer에 의해 인증 헤더에 맨 앞에 추가되어 보내어지고 서버는 응답을 클라이언트에 보내기 전 토큰을 디코딩 함으로서 해당 토큰의 시그니처가 유효한지 검사를 합니다. 헤더의 내용은 이와 같습니다: _Authorization: Bearer
_ -
로그아웃이 되면 클라이언트에 저장되어 있던 토큰은 서버와의 통신없이 삭제되어집니다.
알아두세요, JWT는 인증에 필요한 정보만을 포함합니다. 그리고 Cross-site scripting (XSS)를 방지하기 위해 민감한 정보들은 삭제합니다.
Advantages of token-based authentication approach
-
토큰 인증 방식은 상태를 가지지않습니다(stateless). 웹 서버는 각각 독립적인 토큰의 기록과 유효성을 검사를 요구하는 데이터를 저장할 필요가 없으며 권한(claim)을 통해서 유저의 정보를 전달할 필요가 없습니다. 서버는 성공적인 로그인을 하게되면 토큰에 사인하고 유효한 요청이 들어왔을때 토큰을 확인할 뿐입니다.
-
CORS가 가능한 토큰 인증방식은 다른 서비스와 도메인의 API에 쉽게 접근하게 만듭니다. 즉 API가 iOS, 안드로이드와 같은 웹과 모바일 플랫폼에서 사용되기 쉽게 만듭니다.
-
JWT에는 데이터들이 저장이 되어있습니다. 즉, JWT는 어떠한 타입의 데이터든지 유연하게 저장할 수 있습니다.
-
전체적인 시스템의 성능을 향상시킵니다. 예를 들어, 엔드포인트인 _/api/books_를 사용해 관리자 계정을 가지고 있는 유저만이 접근 가능한 DB에 저장되어 있는 모든 책들의 데이터들을 가져올 수 있습니다. 쿠키 인증방식에서 한번 서버에 요청을 하게되면 세션 아이디안의 쿠키가 유효한지 DB 체크를 한번 해야합니다. 그리고 유저에 관한 데이터를 얻고 해당 유저가 관리자 역할을 하는지 검사하기 위해 한번 더 체크합니다. 그리고 마지막으로 요청했던 데이터를 얻기 위해 세번째로 체크를 해야합니다. JWT 방식에서는 유저의 역할이 JWT안에 저장되어 질 수 있으며 해당 JWT를 해독하는것이 DB에서 요구하는 데이터를 찾는 속도보다 더 빠를겁니다. 그래서 서버에 요청을 하고 JWT의 유효성이 검증이 되면, 단 한번의 요청으로 DB에 저장된 책들의 데이터들을 가져올 수 있습니다.
-
JWT는 유지하기 쉽고 분산된 시스템에서 수평적인 계층(scale horizontally)으로 설정할 수 있습니다.
Limitations of Token-based authentication
-
토큰에 많은 양의 데이터를 저장하는 것은 토큰의 크기를 크게 만들 수 있으며 요청 속도를 늦출 수 있습니다.
-
토큰은 DB에 세션이 존재하지 않으므로 서버의 백그라운드(background)에서 유저 인증을 위해 서용될 수 없습니다.
-
클라이언트 사이드에서의 토큰 저장소는 문제가 될 수 있습니다. 토큰이 쿠키에 저장이 될 때, JWT의 사이즈가 크다면 쿠키 사용이 비효율 적일 수 있습니다. 토큰은 세션 스토리지에 저장이 될 수 있지만 브라우저가 닫히면 지워지게 됩니다. 로컬 스토리지에 토큰을 저장하게 된다면 토큰의 사용은 특정 도메인에만 묶이게 됩니다.
-
클라이언트 사이드에서 토큰은 Cross-site scripting (XSS) 공격에 취약해서 탈취 당할 수 있습니다. 이는 외부의 엔티티가 페이지에 악성 자바스크립트를 삽입하여 브라우저 저장소의 콘텐츠를 읽고 손상시키는 코드를 도메인에서 실행 시킴으로써 발생될 수 있습니다.
When to choose between token and cookie authentication
쿠키와 토큰 인증방식은 어떤 상황에서도 시스템을 완벽하게 지킬 수는 없습니다.
정리하자면 _토큰 인증방식_을 사용하는것이 더 낫습니다.
-
시스템에서 다른 도메인 사용 방식이 필요할때 토큰 인증방식 필요합니다. 예를 들어, 만약 section.com 도메인에서 github.com에 인증 요청을 보내게 된다면 토큰 사용방식이 더 편하겠죠. 이는 분산 시스템, 특히 서버가 다른 도메인에 있지만 여러 도메인에 걸쳐 인증이 필요한 클라우드의 마이크로 서비스를 구축하는데 유용합니다.
-
토큰은 웹이나 IoT 그리고 모바일 기기와 같은 서로 다른 플랫폼에서 사용되는 API에 유용합니다.
쿠키 인증방식은 이럴때 사용하세요
-
유저의 프로필을 유저에 맞출 필요가 있을때 사용하세요. 테마설정처럼 유저의 개인 취향이 필요할 때 DB의 쿠키 세션을 사용하는것이 더 좋습니다. 또한 쿠키는 각기 다른 유저의 취향에 맞는 광고를 보여줄때도 사용됩니다.
-
만약 사이트가 유저가 사이트를 이리저리 이동하는 동안에 행동을 분석하고 기록하는것이 필요하다면 쿠키 인증방식을 사용하는것이 좋습니다. 예를 들어 유저가 최근에 쇼핑사이트에서 본 물건의 목록들이 있습니다.
-
로그인이나 쇼핑 사이트에서의 장바구니 그리고 게임 기록과 같은 세션들은 추적하고 DB에 기록될 필요가 있습니다. 쿠키가 없다면 사이트에서 떠날때마다 항상 로그인을 하거나 페이지가 닫혔을 때 장바구니를 다시 채워야할겁니다.
Conculusion
인증은 인증 된 사용자에게 보호 된 리소스에 대한 액세스 권한을 부여하여 시스템 보안을 향상시킵니다.
이 글에서, 쿠키 베이스의 인증과 토큰 베이스의 인증에 대해 알아보았습니다. 그리고 어떤 인증 방식을 사용함에 따라 나타나는 장점과 단점에 대해서 강조했습니다.