JWT (JSON Web Token)는 웹 표준으로, 두 개체간에 정보를 안전하게 전달하기 위한 작은 JSON 객체이다. 주로 인증 및 권한 부여에 사용되며, 자체적으로 정보를 인코딩하고 서명할 수 있어 데이터의 무결성과 신뢰성을 보장한다.
JWT는 이 세 부분을 Base64Url 인코딩하여 연결한 문자열이고 각 부분은 점(.)으로 구분된다.
인코딩 된 JWT 예시 : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWT는 대표적으로 권한 부여, 인증에 사용된다. 현재 업무에서 활용하고 있는 방식과 개선점을 위주로 작성하려한다.
Access Token: 사용자 인증 및 권한 부여에 사용되는 토큰. 사용자가 로그인하면 서버는 사용자의 인증 정보를 기반으로 Access Token을 생성하고 클라이언트에게 전달한다. 클라이언트는 이 토큰을 저장하고, 서버에 요청을 보낼 때마다 토큰을 함께 전달하여 인증을 수행한다. 서버는 전달받은 Access Token의 유효성을 확인하고, 유효한 경우 해당 요청에 대한 작업을 수행한다. Access Token은 일정 기간(예: 15분, 1시간 등) 동안만 유효하며, 기간이 만료되면 사용자는 새로운 Access Token을 발급받아야 한다.
Refresh Token: Access Token이 만료된 후 새로운 Access Token을 발급받기 위해 사용되는 토큰. 보통 Access Token보다 긴 유효기간을 가지고 있다(예: 하루, 1주일, 1달 등). 사용자가 로그인할 때 서버는 Access Token과 함께 Refresh Token을 발급하고 클라이언트에게 전달한다. 클라이언트는 이 Refresh Token을 저장해두었다가, Access Token이 만료되면 서버에 새로운 Access Token을 요청할 때 함께 전달한다. 서버는 Refresh Token의 유효성을 확인한 후, 새로운 Access Token을 발급한다.
JAVA에서 서버를 개발할 때는 세션저장소를 이용하여 사용자의 상태를 서버에서 관리하는 식으로 구현했었다. 이렇게되면 서버의 저장공간을 낭비하게 되고 대규모 사용자를 처리하기에 서버의 CPU부하가 발생할 수도 있다.
만약 세션저장소를 이용할 때 서버가 MSA등 분산 시스템이 적용되어 구현되어 있다면 로그인서버의 상태를 다른 기능의 서버에서 알 방법이 없다. 토큰을 이용한다면 이런 확장성을 고민할 필요가 대폭 줄어든다.
토큰을 이용할 때 가장 고민되는 부분은 역시 토큰이 탈취되었을때의 대처방안이다. 이 부분은 밑에서 더 자세히 다루도록 하겠다.
* JWT(Token) 의 보안에 관하여
- 개발을 하다가 Access Token(이하 AT)이 탈취되었을 때와 Refresh Token(이하 RT)이 탈취되었을 때를 생각해보게 됐다.
보통 AT는 만료시간이 짧기 때문에 탈취되더라도 크게 문제가 안 될 가능성이 크다.
내가 고민한 부분은 RT가 탈취되었을 때 대응법이다. 여러가지 방법이 존재하는데 이정도만 조합해도 괜찮을 거 같아서 몇가지 나열해 보자면
1. 발급한 순간 발급받은 ip정보를 같이 저장한다. 만약 탈취되어 로그인 시도를 하더라도 발급 시의 ip주소와 다르다면 탈취된 token으로
가정하여 폐기할 수 있다.
2. 만약 토큰을 두개 다 탈취하지 못하고 한개만 탈취한 상황이라면 Refresh 요청을 할 때 만료된 AT와 RT를 둘 다 보내야만 재발행해주는 방법.
우선 이정도의 고민을 해봤지만 사실 이외에도 방법은 몇가지 더 있는 것 같다.
중요한 점은 어떤 기능이든 완벽하지 않아서 계속 보완하는 기능을 조합해야 하는 것 같다.