JWT를 통해 로그인 기능을 구현하면서 들었던 의문점들에 대해 정리한 글입니다.
1. JWT(JSON Web Token)란?
2. 왜 JWT를 사용할까? (Cookie + Session vs JWT)
3. JWT를 사용할 경우 만료 기한은 어느 정도로 지정할 것인가?
4. Refresh Token이 탈취된다면 보안적으로 똑같이 위험하지 않은가?
5. Refresh Token을 저장하고 있어야 하는가?
6. 토큰 탈취 자체를 방지할 수 있는 방법은 없을까?
7. 결론
JWT(JSON Web Token)란?
: 두 개체 사이에서 정보를 JSON 개체로 안전하게 전송하기 위한 컴팩트하고 독립적인 방식을 정의하는 개방형 표준으로, 전자 서명이 되어 있으므로 신뢰할 수 있다.
JWT는 .을 기준으로 하여 Header, Payload, Verify Signature 세 가지로 구분할 수 있다.
- Header
- 토큰에 대한 메타 데이터를 포함 (타입, 해싱 알고리즘 SHA256, RSA ...)
- Payload
- 주고받고자 하는 정보(유저 정보, 만료 기간, 주제 등)를 포함
- Verify Signature
- Header와 Payload가 누군가에 의해 변조되지는 않았는지 검증하기 위해 사용
- 위의 Header와 Payload를 인코딩하여 합친 문자열과 secret key로 헤더의 alg에 정의된 알고리즘을 사용하여 암호화
왜 JWT를 사용할까? (Cookie + Session vs JWT)
로그인 기능을 구현하기 위한 인증 방식으로 크게 Cookie+Session과 JWT로 나눌 수 있다.
각각의 인증 절차와 장단점에 대해 알아보자.
Cookie + Session
인증 절차
로그인 요청 시 사용자를 특정할 수 있는 유니크한 데이터로 세션 ID를 생성하여 사용자에 대한 정보를 세션 저장소에 저장한다.
클라이언트에서는 쿠키에 세션 ID를 저장하여 요청 시에 넘겨주고 , 서버에서는 인증이 필요한 요청이 들어왔을 때 쿠키에 담긴 세션 ID를 통해 검증을 진행하게 된다.
장점
- 사용자에 대한 정보는 서버에 저장되어 있기 때문에 쿠키의 데이터로는 알 수 있는 값이 없기 때문에 보안상 안전하다.
단점
- 서버를 스케일 아웃할 경우 각 서버의 세션을 동기화해야 하는 비용이 추가적으로 발생할 수 있다. 즉, 세션 저장소에서 클라이언트의 상태를 저장하게 되므로 stateless를 위배한다.
- 사용자의 데이터를 서버의 메모리에 저장하기 때문에 메모리 용량에 대한 리스크가 있을 수 있다.
JWT
인증절차
로그인 요청 시 사용자에 대한 정보를 담은 토큰을 생성하여 클라이언트에 전달한다.
클라이언트에서는 요청 시에 해당 토큰을 넘겨주고, 서버에서는 토큰에 대한 유효성을 검사하여 검증을 진행한다.
장점
- 쿠키와 세션과 다르게 별도의 저장소를 필요로 하지 않기 때문에 stateless 하여 서버 확장성이 뛰어나다.
- Facebook, Google 소셜 로그인 등처럼 사용자에 대한 인증 방식의 확장이 가능하다.
단점
- 토큰을 한 번 발급하면 해당 토큰이 만료되기 전까지는 사용을 막을 수 있는 방법이 없다.
- 토큰의 크기가 커지면 네트워크에 부하를 줄 수 있고, Payload는 따로 암호화되지 않기 때문에 필요한 최소한의 정보만 담아야 한다.
=> JWT는 위변조를 방지할 수 있으면서 디비 조회 없이 검증할 수 있으므로 stateless 할 수 있다는 점에서 세션 방식에 비해 가볍고 확장성이 좋다.
JWT를 사용할 경우 만료 기한은 어느 정도로 지정할 것인가?
JWT 방식으로 Access Token을 발급하였을 때 만료 기한에 따라 두 가지 경우가 생길 수 있다.
1. Access Token의 만료 기한을 짧게 설정한 경우
→ 사용자가 매번 재로그인해야 하는 번거로움이 생긴다.
2. Access Token의 만료 기한을 길게 설정한 경우
→ 토큰이 탈취되었을 경우를 대비해 토큰을 포함하는 모든 요청마다 검증을 진행하고, 토큰이 무효화되지는 않았는지 검증하는 과정이 필요해 서버 부하가 커질 수 있다.
그렇다면 어떻게..?
Access Token은 만료 기한을 짧게 하고, 만료 기한이 비교적 긴 Refresh Token을 추가로 발급한다면
사용자 재로그인에 대한 번거로움을 없애고 토큰 무효화 관련 부하를 막을 수 있다.
Refresh Token이 탈취된다면 보안적으로 똑같이 위험하지 않은가?
refresh Token을 사용한다고 보안이 월등하게 좋아지는 것은 아니지만 단독 사용보다는 강화할 수 있다.
- access token은 모든 요청 시에 사용되지만 refresh token은 재발급 요청 시에만 사용되기 때문에 노출될 가능성이 낮다고 볼 수 있다.
- refresh token을 통해 access token을 발급하는 과정에서 다른 나라의 IP 주소로 요청이 들어왔다던가 또는 계정 도용으로 신고된 아이디 등을 검증하여 서버가 해킹된 토큰에 대한 방어 행동을 취할 수 있게 된다.
refresh Token을 사용하는 것은 필수가 아닌 선택이다.
보안성, 성능, 사용편의성 등을 적절하게 타협한 결과이기에 보안을 가장 중요시하게 추구한다면 refresh Token 이외에 더 좋은 방법들이 많이 있을 것이다.
Refresh Token을 저장하고 있어야 하는가?
stateless를 일정 부분 포기하더라도 토큰 파기 로직을 구현할 것이라면 저장이 필요하다.
그렇다면 세션 방식과 다르게 JWT가 가지는 stateless라는 이점이 아예 사라지는 게 아닌가?
→ 어느 정도는 맞다. 그렇지만 토큰을 재발급하는 경우에만 디비 접근이 일어나는 것이므로 전체를 stateful 하게 가져가는 것보다는 가볍게 운영이 가능하다.
토큰 탈취 자체를 방지할 수 있는 방법은 없을까?
1. 토큰을 탈취하기 어렵게 한다.
→ httpOnly/httpOnly cookie에 보관하면 xss 등에 탈취되는 건 막을 수 있지만 컴퓨터가 본격적으로 해킹당하면 방법은 없다.
2. 토큰을 탈취하더라도 사용하기 어렵게 한다.
→ 토큰의 만료기한을 짧게 주거나 특정 ip에서만 사용할 수 있게끔 하는 방법 등이 있을 수 있다.
결론
JWT가 인증 방식에 있어서 만능은 아니다.
각각의 인증 방식이 가지는 장단점이 있으므로,
서비스가 보안과 사용성 중 무엇이 더 중요한지, 서비스의 사용자 수가 많은지, 사용자의 권한 범위가 간단한지 등을 파악하여 서비스에 적절한 인증 시스템을 도입해야 한다.
<참고>