토큰이 도대체 뭐길래.
세션을 공부하니까, 토큰을 알게되었다.
역시 하나로 끝나지 않는 코딩의 세계.
✔️ 인증(Authentication)과 인가(Authorization)의 차이
💡 인증 (Authentication)
말 그대로 내가 누군지 인증하는 것이다.
쉽게 말하면 '로그인'이다.
클라이언트가 자기자신이라고 주장하고 있는 사용자가 맞는지를 검증하는 과정이다.
- 로그인과 같이 사용자 또는 프로세스의 신원을 확인하는 프로세스이다.
- 인증에 사용할 데이터와 저장되어 있던 데이터를 비교한다.
- 이 데이터는 인증 서버에 저장되며, 가장 일반적인 인증 방법은 비밀번호를 사용하는 것이다.
💡 인가 / 권한 부여 (Authorization)
허용된 권한을 말한다.
인가는 인증 작업 이후에 행해지는 작업으로,
인증된 사용자에 대한 자원 접근 확인 절차를 의미한다.
- 누가 무엇을 할 수 있는지 결정하는 규칙이다.
- 사용자가 로그인하면 각 후속 요청에 JWT가 포함되어 사용자가 해당 토큰으로 허용되는 경로, 서비스 및 리소스에 엑세스 할 수 있다.
- JWT가 유용하게 사용되는 시나리오 중 하나이다.
✔️ HTTP의 비상태성(Stateless)
HTTP는 stateless한 특성을 가지기 때문에 각 통신 상태는 저장되지 않는다.
하지마 매번 새 페이지를 요청할 때마다 로그인을 해야 한다면, 사용이 불가능할 것이다.
💡 이 문제를 해결하기 위한 대표적인 도구 두 가지가 세션(Session)과 토큰(Token)이다.
- 유저가 로그인을 시도할 때 서버상에서 일치하는 유저 정보를 찾았다면, 인증(Authentication) 확인의 표시로 세션이나 토큰을 발급/전달 해준다.
- 그럼 웹 브라우저에서 해당 세션/토큰 정보를 받아 간직하고 있다가
- 새로운 request를 보낼 때마다 인가(Authorization)를 위해 해당 세션/토큰을 함께 보낸다.
💡 세션과 토큰 모두 존재 목적은 같지만, 차이점은 존재한다.
- 가장 큰 차이점은 세션은 데이터베이스 서버에 저장된다.
- 토큰은 클라이언트 측에서만 저장된다.
로그인을 통해 인증이 되면, 이제 권한을 부여해야 한다.
이런 권한을 부여하는 과정에서 사용되는 기술이 Session과 JWT이다.
✔️ Session
Session은 서버 기반 인증의 대표적인 예이다.
- 클라이언트가 로그인을 위해 인증 정보를 서버에 전송한다.
- 서버는 메모리에 사용자를 저장하고, 이 안에 로그인한 유저의 정보가 저장이 된다. 세션에 저장된 정보에는 고유의 세션ID가 부여된다.
- 세션 아이디를 쿠키로 전달한다.
- 클라이언트는 쿠키에 저장된 세션 아이디를 이용하여 요청을 보낸다.
- 서버는 일치하는 세션 아이디를 메모리에서 검색한 후 응답을 보내준다.
- 페이지가 이동할 때마다 이 작업이 반복되고, 로그인 상태가 유지되는 것이다.
즉,
- Session 정보는 DB에 저장되며,
- 클라이언트는 SessionID를 가지고 있다.
- 클라이언트는 http request 요청 시 cookie에 sessionID를 저장하여 보내고,
- 서버에서는 전달받는 sessionID로 DB에서 session을 조회한다.
- 클라이언트는 sessionID만 관리하면 된다.
💡 Session의 장점
- 더이상 유저 정보 등을 cookie에 직접 담아서 전송할 필요가 없다.
- cookie에 직접 유저정보를 담게되면 보안상 매우 위험하다.
- sessionID가 탈취되더라도 DB에 저장된 session을 삭제하면 된다.
🚨 서버 인증 기반의 문제점
- 저장 공간의 용량
- 세션을 DB에 저장해서 탐색하기 때문에, 유저가 늘어날수록 서버의 램이 과부화된다.
- 여러 서비스를 유지할 때 A 서비스 sessio, B 서비스 session이 나누어져 있다면, 해당 서비스의 session으로만 사용해야 한다.
- 확장성의 문제
- 여러 개의 프로세스를 돌리거나, 서버 컴퓨터를 추가하는 것이 어려워진다. 즉, 확장성이 좋지 않다.
- 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 있어, 여러 도메인에서 관리하기가 번거롭다.
이를 해결하기 위해 보통 JWT라는 로그인 방식을 도입한다.
✔️ JWT(Json Web Token)
JWT는 Session에 사용자 데이터를 저장하는 전통적인 쿠키 세션 방식과 다르게
JSON 객체에 사용자 정보의 일부를 담는다.
💡 한마디로, JWT는 session, cookie를 대신하는 의미가 있는 문자열 토큰이다.
- 클라이언트가 로그인을 위해 인증 정보를 서버에 전송한다. (동일)
- 서버는 secret 정보를 이용하여 JWT를 생성하고, 클라이언트에게 전달한다.
- 클라이언트는 로컬 혹은 브라우저에 저장해두었던 JWT를 이용하여 요청을 보낸다.
- 서버는 JWT가 일치하는지 확인한 후, 응답을 보내준다.
💡 Token 인증방식의 장점
- 무상태와 확장성
- Token은 클라이언트에만 저장되므로 stateless하며, 서버를 확장하기에 적합한 환경을 제공한다.
- 쿠키를 사용함으로 인해 발생하는 보안 취약점이 사라진다.
- 토큰을 사용하여 다른 서비스에도 권한을 공유할 수 있다.
❓ Session 인증과 비교한 JWT의 차이점
- 가장 큰 차이는 서버에 인증 정보를 저장하지 않는다는 점이다.
- 그렇기 때문에 클라이언트의 요청마다 인증을 위해 DB를 탐색하는 과정이 필요하지 않고,
- 저장 공간도 필요하지 않다.
💡 JWT는 언제 사용하면 유용할까?
- 권한 부여
- JWT를 사용하는 가장 일반적인 방식이다.
- 사용자가 로그인하면 각 후속 요청에 JWT가 포함되어, 사용자가 해당 토큰으로 서비스 및 리소스에 접근할 수 있다.
- 정보 교환
- JWT는 정보를 안전하게 전송하는 좋은 방법이다.
- 예를 들어, 공개/개인 키 쌍을 사용하여 JWT에 서명할 수 있기 때문에 발신자가 누구인지 확인할 수 있다.
- 또한 헤더와 페이로드를 사용하여 서명을 계산하므로, 콘텐츠가 변조되지 않았는지 확인할 수 있다.
✔️ JWT 구조
JWT는 3개의 점(.)으로 구분된 세 부분으로 구성되며 다음과 같다.
header.payload.signature
- Header
- Payload
- Signature
따라서 JWT는 일반적으로 다음과 같다.
xxxxx.yyyyy.zzzzz
💡 Header
헤더는 토큰의 타입(종류)(type)과 어떤 알고리즘(암호화 방식)(alg)이 쓰였는지를 나타낸다.
- 이 json은 Base64Url로 인코딩된 JWT의 첫 번째 부분이다.
- 보통 HMAC SHA256 혹은 RSA가 사용되며, 토큰을 검증할 때 사용한다.
- 헤더에는 토큰의 종류와 SIGNATURE 생성을 위해서 어떤 알고리즘을 사용했는지가 명시되어 있다.
💡 Payload
페이로드에는 사용자, 토큰 정보를 나타내는 클레임(claim)으로 구성되어있고,
key-value 형태이다.
즉, 내가 로그인한 유저임을 증명할 수 있는 기본적인 정보들을 넣는다.
차후에 클라이언트가 다시 토큰을 보내면, 해독해서 DB 내의 유저 정보와 비교한다.
클레임에는 사용자 마음대로 아무런 값을 넣을 수 있다.
(다만, 클레임이 많아질수록 토큰의 길이가 길어진다.)
🚨 payload에 담긴 정보는 인코딩만 되어 있기 때문에,
누구라도 디코딩하여 확인할 수 있으므로 중요한 정보를 담으면 안 된다.
- 페이로드는 내가 로그인한 유저임을 증명할 수 있는 기본적인 정보들을 넣는다.
- 차후에 클라이언트가 다시 토큰을 보내면, 해독해서 DB 내의 유저 정보와 비교한다.
클레임은 3가지의 종류가 있다.
- 등록된 클레임 (registered claim)
- 필수는 아니지만, 운영상 미리 정의된 클레임이다.
- iss(발급자), exp(만료 시간), sub(제목), aud(대상), nbf(토큰의 활성 날짜) 및 기타로 이루어져 있다.
- 공개 클레임 (public claim)
- JWT를 사용하는 사용자들이 마음대로 정의할 수 있다.
- 공개용 정보 전달을 위해 사용된다.
- 충돌 방지를 위해 URI 포맷을 이용해야 한다.
- 비공개 클레임 (private claim)
- 등록되어 있지도, 공개되지도 않은 클레임이다.
- 당사자간에 정보를 공유하기 위해 만들어진 사용자 지정 클레임이다.
- 즉, 이곳에 인증 정보 등의 서버와 클라이언트간 필요한 정보를 넣어두는ㄴ 형식이다.
💡 Signature
시그니처는 인코딩된 헤더, 페이로드 그리고 비밀키(secret_key)를 가지고
헤더에 정의된 알고리즘으로 해싱하여 생성한다.
즉, 토큰을 인코딩하거나 유효성 검증을 할 때 사용하는 고유한 암호화 코드이다.
- Signature은 위에서 만든 헤더와 페이로드의 값을 각각 BASE64로 인코딩하고,
- 인코딩한 값을 내가 설정한 비밀키를 이용해 헤더에서 정의한 알고리즘으로 해싱을 하고,
- 이 값을 다시 BASE64로 인코딩하여 생성한다.
- 🚨 혹시라도 토큰이 제 3자에 의해 탈취되어서 페이로드의 내용이 변경된다면, 토큰의 값이 크게 변경된다.
- 헤더와 페이로드는 암호화된 값이 아니라 base64로 인코딩 된 값이기 떄문에 위 이미지처럼 누구나 확인이 가능하다.
- 그렇기 때문에 토큰을 검증하는 서명(Signature)이 정말 중요하며,
- 서명에 사용되는 secret_key를 잘 관리해야 한다.
✔️ 토큰 작동 방식
- 사용자가 로그인하면 JSON 웹 토큰이 반환된다.
- 🚨 토큰은 자격 증명이므로 보안 문제를 방지하기 위해 세심한 주의를 기울여야 한다.
- 일반적으로 토큰을 필요 이상으로 오래 보관해서는 안 된다.
- 또한 보안이 취약하기 때문에, 민감한 세션 데이터를 브라우저 저장소에 저장해서는 안 된다.
- 앱에서는 설치된 앱 별로 안전한 저장 공간이 제공되지만, 웹에서는 세션 인증이 나은 선택일 수도 있다.
사용자가 보호된 경로 또는 리소스에 액세스하려고 할 때마다
Bearer 스키마를 사용하여 Authorization 헤더에서 JWT를 보내야 한다.
헤더의 내용은 다음과 같다.
Authorization: Bearer <token>
- 이는 특정 경우에 상태 비저장 권한 부여 매커니즘이 될 수 있다.
- 서버의 보호된 경로는 Authorization 헤더에 유효한 JWT가 있는지 확인하고, JWT가 있는 경우 사용자는 보호된 리소스에 엑세스 할 수 있다.
- JWT에 필요한 데이터가 포함되어 있으면 특정 작업에 대해 데이터베이스를 쿼리해야 할 필요성이 줄어들 수 있지만, 항상 그런 것은 아니다.
- 토큰이 Authorization 헤더로 전송되면 CORS(Cross-Origin Resource Sharing)는 쿠키를 사용하지 않으므로 문제가 되지 않는다.
✔️ JWT에 관하여...
- JWT는 DB에 저장하는 방식이 아니기 때문에 DB 리소스가 필요 없다는 장점이 있다.
- 또한 여러 서비스를 운영할 때 jwt 토큰을 사용하면, session처럼 특정 session DB에 접근하는 게 아니라, jwt 토큰 검증 로직을 통해 여러 서비스 간에 통신에서 권한을 쉽게 제한하고 허가할 수 있다는 장점이 있다.
- 🚨 단점으로는
- jwt 토큰이 탈취되었을 경우 누구나 해당 토큰의 권한을 가지고 서비스를 이용할 수 있다.
- 누구나 헤더와 페이로드의 내용을 볼 수 있기 때문에, 보안에 위협이 될만한 정보나 개인정보가 있다면 노출될 수 있다. (그래서 그런 정보를 넣으면 안 된다.)
- 이러한 이유로 보통 jwt 토큰의 만료시간(exp)을 짧게 정하고
- 리프래시 토큰(Refresh Token)과 함께 사용한다.
- 🚨 또 다른 단점으로는
- 토큰에 담고있는 정보가 많아질수록 데이터의 크기가 커진다.
- 그렇게되면, 네트워크 전달시 데이터의 크기로 부하가 생길 수 있다.
- 토큰에 담고있는 정보가 많아질수록 데이터의 크기가 커진다.
- 💡 리프래시 토큰은 DB에 저장되며 긴 만료시간을 가지고 있다.
- 사용자는 jwt 토큰이 만료되면, 리프래시 토큰을 이용해 갱신을 하여 사용하게 된다.
- 앱에서 별도의 재로그인 없이 계속 서비스를 이용할 수 있는 것은 리프래시 토큰 덕분이다.
❓ 만약 리프레시 토큰까지 탈취되면?
→ 비교적 만료시간이 짧은 jwt 토큰은 탈취되어도 금방 만료되기 때문에 보안상 위협이 비교적 적은데 반해,
리프래시 토큰은 새로운 jwt 토큰을 발급받을 수 있기 때문에 탈취되면 위험하다.
💡 이런 경우 토큰의 payload를 확인해서 userid를 블락(block)시키는 방법과,
해당 유저의 리프래시 토큰을 모두 만료시키는 방법이 있다.
✔️ JWT의 장단점
💡 장점
- JWT를 이용하면 따로 서버의 메모리에 저장 공간을 확보할 필요가 없다.
- 서버가 토큰을 한번 클라이언트에게 보내주면, 클라이언트는 토큰을 보관하고 있다가 요청을 보낼 때마다 헤더에 토큰을 실어보내면 된다.
- 쿠키를 사용할 수 없는 모바일 어플리케이션에는 JWT를 사용한 인증방식이 최적이다.
🚨 단점
- JWT는 HTTP를 통해서 전송하기 때문에, 페이로드의 크기가 클수록 데이터 전송에 있어서 비용이 커진다.
- JWT는 유효기간을 따로 정하지 않는 이상 소멸되지 않기 때문에 장기간 방치시 해킹의 위험이 커진다.
출처
- https://hudi.blog/session-based-auth-vs-token-based-auth/
- https://jay-ji.tistory.com/101
- https://hudi.blog/session-based-auth-vs-token-based-auth/
- https://fierycoding.tistory.com/69
- https://hayeon1549.tistory.com/38
- https://devjem.tistory.com/13
'프로젝트' 카테고리의 다른 글
JPA 1 : N 관계를 분석해보자 (0) | 2023.02.27 |
---|---|
스프링 시큐리티를 분석해보자 (0) | 2023.02.25 |
Custom Exception 해보기 (0) | 2023.02.19 |
[JPA/Thymeleaf] 게시글 작성자만 수정 권한 가지기 (0) | 2023.02.18 |
게시물 조회수 구현, @Query (0) | 2023.02.17 |