우선 위 방식을 알아보기 전에, 인증과 인가에 대한 정의를 짚고 넘어가 봅시다.
인증과 인가
인증(Authentication)은 로그인이고, 인가(=권한 부여, Authorization)는 로그인한 유저의 권한 검증(관리자 페이지 접근)입니다.
즉, 인증은 자신이 누구인지 증명하는 것을 의미하고, 인가(권한 부여)는 인증된 주체에게 특정 작업을 수행할 수 있는 권한을 확인하는 것을 의미합니다.
참고로 HTTP 상태코드에서 401(Unauthorized)은 인증과 관련된, 403(Forbidden)은 인가와 관련된 오류 상태 코드입니다.
이제 인증과 인가를 알았으니 어떻게 구현할 수 있을까요?
로그인을 분명히 했는데도 불구하고 페이지 이동에 따라 로그인이 풀려 계속 다시 로그인을 해야 한다면 사용자 입장으로서 엄청 불편함을 느끼겠죠. 그래서 우리는 로그인한 사용자가 로그인이 되어있음을 지속하기 위해 다양한 방식으로 구현합니다.
대표적으로 세션 기반 인증, 토큰 2가지 방식이 있습니다.
세션 기반 인증 (= 쿠키 기반 인증)
stateful, 서버가 사용자의 인증 기록을 유지 관리하는 방법입니다.
세션 기반 인증 동작 방식
1. 사용자 인증 : 클라이언트는 서버에게 사용자가 인증 정보를 전송하고 서버가 이를 검증합니다.
2. 세션 생성 : 인증이 성공하면 서버는 unique identifier, user identifier, session start time, expire와 user agent, ip 주소 같은 추가 context를 포함하는 session id를 생성하고 이를 DB에 저장하고 클라이언트에 session id를 전송합니다.
(쿠키의 보안적인 이슈 때문에 비밀번호, 클라이언트의 민감한 인증 정보는 브라우저가 아닌 서버 측에 저장하고 관리합니다.)
3. 쿠키 저장 : 클라이언트는 서버로부터 받은 session id를 쿠키로 저장합니다.
4. 세션 검증 : 추후 클라이언트는 요청을 보낼 때 쿠키 내 세션 아이디를 포함해 전송하고, 서버는 데이터베이스에 쿼리 하여 세션을 검증합니다. 유효한 경우 요청이 처리됩니다.
세션 기반 인증의 장점
- 단순성과 신뢰성 : 서버의 세션 기록은 중앙화된 truth source 역할을 해 사용자 세션을 관리하기 쉽습니다.
- 해지 효율성 : 세션 기록을 삭제하거나 무효화하여 access를 신속하게 해지할 수 있어 최신 세션 유효성이 보장됩니다.
예를 들면, 강의 사이트에서 사용자의 접속 기기를 관리할 때 세션 기반 인증을 사용한다면 다른 기기에서 접속 시 세션을 해제시켜 버리는 처리가 가능해집니다. 이렇게 즉각적인 제어가 중요한 애플리케이션의 경우에는 세션 기반 인증을 선호합니다.
세션 기반 인증의 단점
- 대규모 환경에서의 성능 문제 : 모든 세션 검증을 위한 DB 상호작용에 대한 종속성으로 지연이 발생할 수 있으며, 특히 트래픽이 많은 애플리케이션의 경우 성능 문제가 발생할 수 있습니다.
- 동적 환경에서의 지연 시간 : 동적 클라이언트가 있는 애플리케이션(자주 변경되거나 다양한 상태를 요구하는 사용자 인터페이스를 가진 앱을 의미)에서 이러한 지연 시간은 사용자 경험에 영향을 미치기 때문에 이러한 시나리오에서 세션 기반 인증은 이상적이지 않을 수 있습니다.
쿠키
- 서버에서 생성되어 클라이언트 측에 저장되는 데이터로 상태를 유지하지 않는 HTTP의 특성을 보완하기 위한 수단입니다. 즉, 서버가 클라이언트의 상태를 알 수 있게 하는 데이터입니다. 각 사용자마다 브라우저에 정보를 저장해 고유 정보 식별이 가능합니다.
- Key-Value 형식을 띠고 있고, 적용 범위와 만료 기간 등 다양한 속성을 가질 수 있습니다.
- 서버는 쿠키를 생성해 클라이언트에게 전송하고, 클라이언트는 전달받은 쿠키를 저장해 두었다가 추후 동일한 서버에 보내는 요청 메시지에 쿠키를 포함해 전송합니다. 서버는 쿠키 정보를 참고해 두 개의 요청이 같은 클라이언트에서 왔는지, 로그인 상태를 유지하고 있는지 등을 알 수 있습니다.
- 단, 쿠키는 요청 시 쿠키의 값을 그대로 보내기 때문에 보안에 취약하고, 용량 제한이 있어 많은 정보를 담을 수 없습니다. 또한, 웹 브라우저마다 쿠키에 대한 지원 형태가 달라 브라우저간 공유가 불가능합니다. 쿠키의 사이즈가 커질수록 네트워크에 부하가 심해진다는 단점도 있습니다.
JWT
JWT는 JSON Web Token의 약자로 JSON 객체로 정보를 안전하게 전송하기 위한 독립적인 메커니즘 역할을 합니다.
서버는 클라이언트가 저장하고 각 요청과 함께 제공하는 토큰을 생성한다. stateless, 서버는 토큰 기록을 보관할 필요가 없습니다.
JWT 동작 방식
1. 사용자 인증 시 서버는 JWT를 생성합니다.
2. JWT는 클라이언트로 다시 전송되어 저장되는데, localStorage나 HTTP 전용 쿠키에 저장됩니다.
3. 클라이언트는 이 토큰을 후속 요청에 대한 HTTP 인증 헤더에 포함시킵니다.
4. 서버는 토큰을 검증하고 유효한 경우 access를 허용합니다.
JWT의 구조
- Header : token type(JWT)과 signing algorithm(HMAC SHA256, RSA)을 지정합니다.
(signing algorithm은 Signature를 만드는 데 사용될 알고리즘이 지정됩니다.)
- Payload : claim(토큰에 담긴 사용자 정보 등의 데이터)이 담깁니다. (entity(사용자)에 대한 정보와 metadata)
(Base64 디코딩하면 정보를 알 수 있습니다.)
- Signature : secret으로 header와 payload를 인코딩해 토큰의 무결성을 보장합니다.
(secret은 서버에 감춰놓은 비밀값을 의미합니다.)
JWT의 장점
- 확장성 : JWT는 무상태 특성으로 인해 분산 시스템에 이상적입니다. (서버 간 부하 분산(로드 밸런싱)이 더 용이합니다.)
- 유연성 : 다양한 도메인과 애플리케이션에서 사용할 수 있습니다.
- 보안 : 적절하게 구현하면 사용자 인증을 처리하는 안전한 방법을 제공합니다.
JWT의 단점
- 전송 보안 : HTTPS를 통해 JWT를 전송해야 합니다.
- 저장 : XSS 공격 및 기타 취약성을 방지하기 위해 JWT를 안전하게 저장해야 합니다.
일반적으로 localStorage는 XSS(Cross-Site Scripting) 공격에 취약하기 때문에 권장되지 않습니다.
HTTPOnly 쿠키는 XSS 공격에 좀 더 안전하지만 CSRF(Cross-Site Request Forgery) 공격에 취약할 수 있습니다.
보통 JWT를 활용해 구현하게 되면, 서버 측에서 access Token, refresh Token으로 구현하게 됩니다.
이는 토큰 만료 처리를 위한 방식입니다. 단기 JWT를 구현하고 refresh token을 사용해 재인증 없이 access를 갱신합니다.
access Token으로는 몇 분에서 몇 시간처럼 짧은 기간을 설정해 놓고, refresh Token은 며칠에서 몇 주처럼 긴 기간을 설정해 둡니다.
즉, 이를 통해 access Token이 만료되었을 때, 사용자는 다시 로그인 할 필요 없이 refresh token으로 새로운 access token을 발급 받을 수 있습니다.
마무리하며
요즘은 쉬운 구현 방식 등으로 JWT가 많이 선호되고 있지만, Session 기반 인증 방식이 필요한 경우도 분명히 존재함을 알 수 있었습니다.
JWT와 세션 기반 인증 중 어느 것을 선택해 구현해야 할지는 애플리케이션의 요구사항에 따라 달라질 수 있습니다.
무상태와 확장성을 중시하는 경우 JWT를, 세션에 대한 즉각적인 제어가 중요한 애플리케이션의 경우 세션 기반 인증 방식이 적합합니다.
공통점은 JWT, 세션 기반 인증 모두 서버에서 생성되고 요청마다 검증되어야 한다는 점입니다.
또한 JWT를 사용하더라도 세션 데이터를 서버에 보관해 보안 및 감사 목적으로 활용하는 경우도 있다고 하니 필요에 따라 활용할 수 있음을 알 수 있었습니다.
참고
- https://dev.to/codeparrot/jwt-vs-session-authentication-1mol
- 혼자 공부하는 네트워크
댓글