개발

세션 vs JWT, 인증과 인가

돈벌자돈 2023. 8. 19. 03:04

세션과 JWT가 왜 필요한지 먼저 알아보자.

인증과 인가 (Authentication and Authorization)

우리가 웹서비스를 이용할 때 로그인이라는 과정은 굉장히 흔한 과정이다.

이 로그인이라는 과정이 인증이 되는 것이다.

특정 서비스에 접근할 수 있는 권한을 가진 사용자임을 아이디와 패스워드를 통해 확인받는 과정을 인증(Authentication)이라고 한다.

 

그렇다면 인가(Authorization)는 무엇일까?

우리는 로그인을 거쳐서 웹서비스에 진입했다.

해당 서비스에는 여러가지 기능이 있을 것인데, 여기서 서버가 특정 기능에 대한 접근 요청에 대해 해당 사용자가 권한이 있음을 확인하고 접근을 허가해주는 것을 인가(Authorization)라고 한다.

 

Stateless

우리가 사용하는 웹서비스는 HTTP프로토콜을 기반으로 설계되어있다.

그리고 이 HTTP프로토콜은 Stateless하다는 특성을 가지고 있다.

 

Stateless하다는 것은 쉽게 말해서 모든 요청이 이전 요청과 독립적으로 다뤄진다는 이야기다.

즉, 서버는 새로 들어온 요청이 누구의 요청인지 알지 못한다는 것.

그럼 인가를 해줄 수가 없으니 사용자가 서버에 계속 본인이 누구인지 알려줘야한다. 이 알려주는 방법이 세션과 JWT가 있는 것이다.

 

세션(Session)

세션은 전통적으로 많이 사용돼오던 방식이다.

 

영화관 티켓을 떠올리면 되는데, 사용자가 로그인을 하면 서버가 티켓을 반을 찢어서 사용자의 브라우저에 반주고 나머지 반은 서버가 메모리에 저장해서 보관하게 된다. 그리고 사용자가 새로운 요청을 보낼때 세션ID를 같이 보내 본인이 인증된 사용자임을 서버에 알리고 서버는 메모리를 확인해서 인가해주는 방식이다.

 

다만 이 세션방식에는 문제점이 있는데

 

1. 많은 동시접속자가 발생하면 메모리에 굉장한 부하가 온다.

2. 메모리는 휘발성이라 서버가 재부팅 되면 로그인정보가 모두 날아간다.

3. 그렇다고 메모리의 한계를 극복하기 위해 하드디스크와 같은 저장장치를 사용하면 너무느리다.

4, 대용량 서비스의 경우 여러 대의 서버를 두는 로드밸런싱을 하게 되는데 이 경우 각 서버가 세션ID를 공유하지 못해 사용자의 세션유지를 할 수 없다.

 

4번 문제의 경우 Redis와 같은 메모리형 DB 서버를 사용하면 해결할 수 있긴하다.

하지만 서버가 복잡한 환경 속에서 상태를 기억해야 한다는 것은 역시나 부담이기에 Redis가 근본적인 해결책은 아니다.

 

JWT (JSON Web Token)

상태를 기억해야하는 세션의 문제점을 해결하기 위해 등판한 JWT

 

JWT는 어떤 기술일까?

 

JWT는 세션처럼 티켓을 서버가 보관하지 않는다. 걍 토큰하나 발행해서 사용자에게 쥐어주고 끝이다. 즉 상태를 기억하지 않는다는 것이 핵심.

 

JWT는 암호화된 3가지 데이터를 이어붙인 것이다.

사진처럼 Header, Payload, Signature로 나뉘어있는 이 알수없는 문자열이 JWT이다.

 

1. 헤더(Header)는 해당 토큰이 어떤 유형의 토큰인지, 사용한 암호화 알고리즘이 무엇인지를 알려준다.

2. 페이로드(Payload)는 Base64로 디코딩하면 누가 누구에게 이 토큰을 발행했는지, 토큰의 유효기간, 서비스가 사용자에게 공개하기 원하는 내용(닉네임, 레벨, 관리자여부 등) 등의 정보들을 알 수 있다. 그리고 이 데이터들을 Claim이라고 한다.

3. 서명(Signature)의 역할은 다음과 같다. 서버가 토큰을 받으면 헤더, 페이로드, 서버에 감춰놓은 비밀값을 헤더에 명시된 알고리즘에 넣고 돌리게 된다. 그리고 그 값이 서명(Signature)과 일치하면 해당 토큰을 유효한 토큰으로 간주하고 인가하게 된다.  즉 이게 핵심이다.

 

즉 서버가 가지고 있는 비밀값을 알지 못하면 누군가 페이로드를 변경하여 정보를 조작하려해도 암호화 알고리즘에 넣고 돌리면 해당 값이 서명과 일치하지 않게 되므로 조작이 불가능해진다.

 

따라서 세션과 달리 서버가 상태를 기억할 필요 없이 서버에 저장된 비밀값으로 들어온 토큰을 스캔해서 유효한지 아닌지만 체크해주면 되니까 부담을 굉장히 덜은 셈.

 

이런 JWT가 세션보다 모든 부분에서 우월할 것 같지만 또 그렇지는 않다...

 

서버가 상태를 저장하지 않는다는 이야기는 반대로 생각해보면 해당 상태를 서버가 제어할 수 없다는 이야기다.

예를들어 어떤 웹서비스에 PC를 통해 로그인했다고 치자. 그리고 바로 모바일을 통해 다시 같은 사용자로 로그인했을때 PC에 로그인 된 사용자는 로그아웃처리를 해줘야 하는데 서버에서는 세션처럼 상태를 저장하지 않기때문에 아무 동작도 할 수 없게된다.. 그리고 토큰이 탈취되었을때 해당 토큰을 무효화할 방법도 없다.

 

물론 토큰 탈취의 경우에는 보완할 방법이 있다. Access토큰과 Refresh토큰 두가지 유형의 토큰을 사용하는 것이다.

 

Access토큰 : 유효기간 짧게 설정

Refresh토큰 : 유효기간을 Access토큰보다 상대적으로 길게 설정

 

1. Access토큰과 Refresh토큰을 발급하고 클라이언트에게 보냄
2. 서버는 Refresh 토큰은 상응값을 데이터베이스에도 저장
3. 이후 사용자의 Access토큰이 수명을 다하면 Refresh토큰을 데이터베이스에 저장한 값과 대조해보고 유효하다면  새로운 Access토큰을 발급

 

물론 Access 토큰이 살아있는 짧은 시간 동안은 바로 차단이 불가능하다. 즉, 확실한 해결책이라 보긴 어렵다.

 

결론: 세션, JWT 모두 장단점이 있는 친구들이므로 서비스의 성격을 잘 따져보고 사용하도록 하자~