토큰 (Token)
토큰(Token)은 웹 상에서 특정한 목적으로 사용 가능한 동전에 일종의 권한를 주는 것을 말합니다. 이러한 토큰은 일반적으로 안전하게 전송되고 검증되며, 클라이언트 애플리케이션과 웹 서비스 또는 API 간의 통신을 안전하게 관리하는 데 도움이 됩니다. 클라이언트가 서버에게 받는 일종의 입장권이라고 생각하시면 편합니다.
token 사용 목적
Stateless
토큰 기반 인증은 사용자의 정보를 서버나 세션에 저장하지 않습니다.
stateless server는 client의 요청만으로 사용자의 권한을 검증할 수 있기 때문에 서버의 확장에도 유리합니다.
세션 기반 인증을 사용한다면, 세션 정보를 DB에 담아 사용자의 권한을 확인할때마다 DB를 조회해야 하기 때문에 성능 측면에서도 불리하고, 서버 확장시 세션을 공유할 수 있는 기술이 필요합니다.
사용자 인증 방식 ( session vs token)
세션 기반 인증
서버의 세션을 사용해 사용자 인증을 하는 방법으로 서버측(서버 램 or 데이터베이스)에서 사용자의 인증정보를 관리하는 것을 의미합니다. 클라이언트로부터 요청을 받으면 클라이언트의 상태를 계속에서 유지해놓고 사용합니다. 이는 사용자가 증가함에 따라 성능의 문제를 일으킬 수 있으며 확장성이 어렵다는 단점을 지닙니다.
하지만 세션 정보를 서버쪽에서 관리하기 때문에 클라이언트의 변조에 영향을 받지 않고, 클라이언트가 서버에게 요청할때 담는 데이터인 sessionId에는 사용자의 정보가 존재하지 않기 때문에 주고받는 데이터도 적고, 보안에도 유리합니다.
token 기반 인증
인증받은 사용자에게 토큰을 발급하고, 로그인이 필요한 작업일 경우 헤더에 토큰을 함께 보내 인증받은 사용자인지 확인합니다. 이는 서버 기반 인증 시스템과 달리 상태를 유지하지 않으므로 Stateless 한 특징을 가지고 있습니다. 또한 인증이 필요한 모든 요청에 세션 아이디를 통한 조회가 필요한 세션 기반 인증방식과 달리 서버에 부담을 적게 줄 수 있습니다.
token 기반 인증 방식의 과정
1. 사용자가 아이디와 비밀번호로 로그인
2. 서버 측에서 사용자(클라이언트)에게 토큰 발급
3. 클라이언트는 서버 측에서 전달받은 토큰을 쿠키나 스토리지에 저장해 두고, 서버에 요청을 할 때마다 해당 토큰을 HTTP 요청 헤더에 포함시켜 전달
4. 서버는 전달받은 토큰을 검증하고 요청에 응답. 토큰에는 요청한 사람의 정보가 담겨있기에 서버는 DB를 조회하지 않고 사용자를 알 수 있음.
JWT( JSON Web Token)은 인증 및 정보 교환을 위한 간단한 토큰 기반의 보안 메커니즘입니다. 이것은 JSON 형식으로 인코딩된 데이터로 구성되며, 서버와 클라이언트 간의 인증 및 권한 부여에 사용됩니다.
jwt 구조
압축된 형태의 jwt는 점(.) 으로 구분된 세 부분으로 구성됩니다.
1. HEADER
헤더는 JWT의 첫 부분으로 나타나며, 일반적으로 두 부분으로 구성됩니다. 이 정보를 기반으로 JWT의 내용을 검증하고 해석하는 데 필요한 정보를 제공합니다. 예를 들어, "alg" 값이 "HS256"로 설정되어 있으면, JWT의 내용을 서버에서 검증할 때 SHA256 알고리즘을 사용하여 확인해야 합니다.
2. PAYLOAD
클레임(claims)을 포함하는 페이로드(payload)입니다. payload에서 서버와 클라이언트가 주고받으며 실제로 사용하고자 하는 정보를 담고 있습니다. Registered claims (사전 정의된 클레임), Public claims (사용자 정의 클레임), Private claims(비공개 클레임)으로 이루어져 있으며, 보통 public claims를 활용하여 원하는 정보를 전달합니다.
주의!! jwt 토큰은 누구나 읽을 수 있기 때문에 중요한 정보를 담지 않아야 한다!!
3. SIGNATURE
signature 부분은 base64로 인코딩된 헤더&페이로드와 서버가 갖고있는 key 값을 합쳐 암호화한 값으로 이루어져있습니다. 이 부분은 메시지가 변조되지 않았음을 검증하기 위해 사용되며, 무결성을 보장하기 위한 가장 중요한 부분이기도 합니다. signature 검증에 실패하면 이 토큰은 유효하지 않은것으로 간주됩니다.
JWT 인증 방식
jwt의 인증 과정은 앞서 설명했던 token 인증 방식과 동일합니다. 다만 client가 authorization server에 권한을 요청하고 허용된 권한에 대한 access token을 부여받게 되는데, 이를 jwt로 구현하여 사용하는 경우가 많습니다.
예를들어, OpenID coonect 를 사용하는 authorization server에서 authroization code 흐름의 /oauth/authorize api(엔드포인트)를 통과하게 되면, 권한을 부여하고 서버가 클라이언트에게 access token(jwt)을 부여하게 됩니다.
JWT의 보안 이슈
1. alg : none 공격
Header의 alg(algorithm) 부분을 none으로 해놓고 만들어서 쓰면 쉽게 공격에 당할 수 있습니다.
2. 변환이 쉽다.
- jwt는 decoding이 매우 쉽기 때문에 민감한 유저 정보를 그대로 넣으면 안되고 최소한의 정보만 넣는게 좋습니다.
3. secret key 문제
signature 부분의 secret key 부분은 보통 개발자가 정해서 server에서 관리하는데, 이걸 대충 정하면 공격당할 수 있습니다. brute force attck(그냥 1부터 100까지 다 때려맞춰보는 공격)에도 뚫리기 쉽고 털리게 되면 jwt를 마음대로 발행하여 사용하게 될 수 있습니다. 더 안전하게 사용하려면 jwt 라이브러리를 활용하여 secret key를 private/public key 로 나누어, 생성용 키와 검증용 키를 따로 사용하는 방법도 있습니다.
4. jwt 탈취
JWT는 client(local storage / cookie)에 저장됩니다. 이 jwt를 탈취당하는 경우가 있는데 이건 잃어버린 사람(client) 잘못이니까 걱정하지 않으셔도 됩니다. 신용카드 잃어버렸다고 카드회사 잘못은 아니잖아용?
하지만 훔친 사람이 신용카드로 매일매일 사용하면 문제가 될수는 있습니다. stateless jwt는 중간에 정지시키기가 힘들고 이를 해결하기 위한 여러 솔루션이 존재합니다.
1. 훔치기 어렵게 만들자 (httponly cookie)
2. 입장권 블랙리스트 운영하자
- jwt blacklist를 서버에 저장해놓고 특정 jwt는 권한을 주지 않는 방식
- 근데 jwt 확인할때마다 DB(blacklist) 확인할거면 session 쓰지 왜 jwt 씀 ㅠ
3. jwt의 유효기간을 매우 짧게 가져가자
- 유효기간을 짧게 가져가서 access token을 탈취당해도 오래 못쓰게 하는 방식
- 근데 이런거 쓰려면 refresh token도 있게 해서 이걸 가지고 있으면 새로운 access token을 발급받을 수 있게 해야 함
- refresh token까지 탈취당하면 진짜 큰일납니다. (refresh token rotation 필요)
- refresh token을 재발급 요청 or 재사용 하려고 하면 token을 주지 말아야 합니다.
-refresh token은 언제나 1회용으로 써야합니다.
대부분의 서비스는 역사와 전통이 있는 세션 방식으로 인증을 진행합니다. 하지만 회원이 너무 많아서 db에서 일일히 입장권 목록(session storage) 확인해가면서 권한 부여하려면 너무 리소스가 많이 들수도 있을 때, 혹은 MSA 구조다 하면 JWT 쓰는게 더 좋을 수 있습니다.
Reference
https://jwt.io/introduction
https://www.youtube.com/watch?v=XXseiON9CV0
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-JWTjson-web-token-%EB%9E%80-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC
https://simsimjae.tistory.com/482
https://velog.io/@hahan/%ED%86%A0%ED%81%B0%EC%9D%98-%EC%A0%95%EC%9D%98%EC%99%80-%EC%A2%85%EB%A5%98
'개발이야기' 카테고리의 다른 글
Cookie와 Session (0) | 2023.08.27 |
---|
댓글