개발 이야기3분 읽기

온라인 JWT 디코더에 운영 토큰을 붙여넣어도 될까

JWT는 암호화가 아니라 서명된 데이터입니다. 디코딩과 검증의 차이, 토큰을 붙여넣기 전에 확인할 것을 정리했습니다.

로그인이 안 될 때 토큰 안을 들여다봐야 하는 경우가 있습니다. 어떤 클레임이 들어 있는지, 만료 시각이 언제인지 확인하려는 것입니다. 검색해서 나온 디코더에 토큰을 붙여넣기 전에 알아둘 것이 몇 가지 있습니다.

JWT는 암호화되어 있지 않다

JWT는 점으로 구분된 세 부분입니다.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NSJ9.dBjftJeZ4CVP
   헤더            .    페이로드      .   서명

앞의 두 부분은 Base64url로 인코딩되었을 뿐 암호화되지 않았습니다. 키 없이 누구나 읽을 수 있습니다. 서명은 내용을 감추는 장치가 아니라 내용이 바뀌지 않았음을 확인하는 장치입니다.

그래서 페이로드에는 남이 봐도 되는 것만 넣습니다. 주민등록번호, 전화번호, 내부 시스템의 권한 이름 같은 것은 들어가면 안 됩니다.

디코딩과 검증은 다른 일

  • 디코딩 — Base64url을 풀어 내용을 보는 것. 키가 필요 없습니다
  • 검증 — 서명이 맞는지 확인하는 것. 키가 필요합니다

디코더가 보여주는 페이로드는 서명을 확인하지 않은 값입니다. 화면에 "role": "admin"이 보여도 그 토큰이 실제로 관리자 권한을 갖는다는 뜻은 아닙니다. 아무나 만들어 낸 문자열이라도 형식만 맞으면 그대로 표시됩니다.

서버 코드에서도 같은 실수가 나옵니다. 서명 검증 없이 페이로드를 파싱해 권한 판단에 쓰면 인증이 없는 것과 같습니다.

붙여넣기 전에

토큰 자체가 자격 증명입니다. 유효기간이 남은 액세스 토큰은 그것만 있으면 해당 사용자로 요청을 보낼 수 있습니다. 비밀번호를 붙여넣는 것과 다르지 않습니다.

입력을 서버로 보내는 디코더라면 그 값이 상대 서버의 로그에 남을 수 있습니다. 운영 환경 토큰이라면 만료 전까지 유효한 채로 남습니다.

확인할 것은 셋입니다.

그 페이지가 입력을 서버로 보내는가. 브라우저 개발자 도구의 네트워크 탭을 열어 두고 붙여넣으면 요청이 나가는지 보입니다.

HS256 검증에는 비밀키가 필요합니다. 이건 토큰보다 위험한 값입니다. 유출되면 임의의 토큰을 만들 수 있습니다.

RS256 검증에는 공개키만 필요합니다. 공개키는 노출되어도 됩니다.

내용만 보면 되는 상황이라면 서명 부분을 떼고 앞의 두 조각만 붙여넣어도 됩니다.

무엇을 보게 되나

페이로드에서 자주 확인하는 값들입니다.

  • exp — 만료 시각. 이 시각이 지나면 거부되어야 합니다
  • iat — 발급 시각
  • nbf — 이 시각 전에는 유효하지 않습니다
  • iss, aud — 발급자와 대상. 다른 서비스용 토큰이 들어오는 것을 막습니다
  • alg — 헤더에 있는 서명 알고리즘

expiat는 밀리초가 아니라 초 단위 유닉스 타임스탬프입니다. 밀리초로 다루는 언어에서 그대로 비교하면 만료 판정이 어긋납니다.

alg는 검증하는 쪽에서 미리 정해 둔 값만 허용해야 합니다. 토큰에 적힌 값을 그대로 믿으면 algnone으로 바꾼 토큰이나, RS256을 HS256으로 바꿔 공개키를 비밀키 자리에 쓰게 만드는 토큰을 받아들이게 됩니다. 요즘 라이브러리는 대부분 막아 두었지만 검증 코드를 직접 쓴다면 확인할 부분입니다.

정리

  • 재현이 가능하면 만료된 토큰이나 개발 환경 토큰으로 확인합니다
  • 운영 토큰을 봐야 한다면 처리 위치가 분명한 도구를 씁니다
  • 확인이 끝난 토큰을 이슈나 메신저에 그대로 남기지 않습니다. 만료 전이라면 살아 있는 자격 증명입니다

tools.onuel.dev의 JWT 도구는 붙여넣은 값이 브라우저 안에서만 처리되고 서버로 전송되지 않습니다. 디코딩서명 검증이 나뉘어 있어, 키 없이 내용만 볼 때와 키를 넣어 확인할 때를 구분해 쓸 수 있습니다.

  • #JWT
  • #인증
  • #보안