면접에서 물어본 질문

면접에서 JWT에 관련된 많은 질문들을 받았는데, 그 중에 이런 질문이 있었다. "facebook이나 Instagram의 경우는 장기간 로그인이 유지된다. 만약 JWT를 유효기간을 설정하지 않을 경우, 보안에 취약할 것으로 보이는데 어떻게 구현되어 있을지 설명할수 있겠는가?"
분명 JWT와 유효기간 설정에 대해서 배우고, 프로젝트에서 인증 / 인가를 구현하면서 생각해본 문제였으나 당시엔 바빠서 따로 찾아보지도 않았다. 이번 면접을 계기로 JWT를 정리하고자 한다.

JWT의 구성

JWT는 Header / Payload / Signature의 세부분으로 구성되어있으며, 각 세부분은 '.'으로 구분된다.

  1. Header : type과 algorithm으로 구성된다.

    {
     "typ" : "JWT",
     "alg" : "HS256"
    }
  2. Payload : 각 사용자의 인증에 사용할 정보를 담는다.
    단 이 Payload에는 사용자의 개인정보를 담을 수 있는 어떤 내용도 담아서는 안된다.
    이부분은 Base64로만 인코딩되기 때문에, 이를 복원하면 시그니쳐의 내용이 없더라도 이 내용을 알아낼 수 있어, 사용자의 개인정보가 유출될 위험성이 있기 때문이다.
    그래서 외부에 유출되어도 상관이없으면서도 고유한 값인 id값, 그리고 해당 토큰의 만료시각 정도를 넣는다.

  3. Signature : 헤더의 인코딩값과, 정보의 인코딩값을 합친후 주어진 비밀키로 해쉬를 하여 생성한다.

이렇게 만들어진 세개의 코드를 '.'으로 연결하면 JWT가 완성된다.

JWT(Access Token) + Refresh Token

이제 원래 공부하려고 했던 내용이다. 만약 사용자 편의성을 위하여 JWT의 만료시간을 엄청 길게 가져간 상황에서, JWT가 탈취당한다면 해당 토큰이 만료되기 전까지는 해당 유저인 척하며 여러 정보를 접근해갈 수 있다.

이를 보완하는 개념이 Refresh Token이다.

클라이언트는 AccessToken이 만료되었다는 오류를 받으면 따로 저장해두었던 RefreshToken을 이용하여 AccessToken의 재발급을 요청한다. 서버는 유효한 RefreshToken으로 요청이 들어오면 새로운 AccessToken을 발급하고, 만료된 RefreshToken으로 요청이 들어오면 오류를 반환해, 사용자에게 로그인을 요구한다.

Refresh Token의 경우는 JWT와 달리 DB에 저장하며, 문제가 생길 경우, 강제로 만료시킬 수 있다.

Access Token과 Refresh Token을 모두 사용하여 인증하는 로직의 순서는 다음과 같다.

  1. 최초 로그인 시에 AccessToken / RefreshToken을 모두 발급한다.
  2. 이후 AccessToken을 가지고 요청한다.
  3. AccessToken이 만료되었을 경우에, RefreshToken을 가지고 AccessToken을 재요청하여발급받는다.
  4. 만약 RefreshToken이 만료되었을 경우, 로그인을 요청하고 AccessToken/RefreshToken을 다시 발급시킨다.

여기서 궁금한점은 RefreshToken을 클라이언트의 안전한 저장소에 저장하라고 나와있는데, 과연 안전한 저장소는 무엇인가? 였다.

이걸 가지고 주위에 물어도보고 알아본 결과는,

  1. 완벽히 안전한 저장소 따위는 이 세상에 존재하지 않는다.
  2. Cookie에서 HttpOnly 옵션을 사용하여 어느정도 보안을 챙긴다.

그렇다면 HttpOnly는 무엇인가?

자바스크립트의 document.cookie를 통해서 쿠키를 전송하는 행위자체를 막는것.