JWT

JWT(Json Web Token)은 토큰 기반 인증 방식으로, 사용자의 세션 상태를 저장하는 게 아니라 필요한 정보를 토큰 body에 저장해 사용자가 가지고 있고 그것을 증명서처럼 사용합니다. 사용자는 Access Token(JWT Token)을 헤더에 실어 서버로 보내게 됩니다.

토큰을 만들기 위해서는 Header, Payload, Verify Signature가 필요합니다.

  • Header: Header, Payload, Verify Signature 정보를 암호화할 방식(alg), 타입(type) 등이 들어감
    • JWT인 토큰의 유형이나 HMAC SHA256 또는 RSA와 같이 사용되는 해시 알고리즘이 무엇으로 사용했는지 등 정보가 담김
  • Payload : 서버에서 보낼 데이터가 들어감. 일반적으로 사용자의 ID, 유효기간이 포함
    • 클라이언트에 대한 정보, META data같은 내용이 들어있고, Base64로 인코딩 되어있음
  • Verify Signature : Base64 방식으로 인코딩한 Header, payload 그리고 SECRET KEY를 더한 후 서명됨

위 내용을 가진 최종적인 형태는 Encoded Header.Encoded Payload.Verify Signature (xxxx.yyyy.zzzz) 입니다.

header와 payload는 base64로 인코딩만 되므로 누구나 디코딩하여 확인할 수 있습니다. 따라서 payload에는 중요한 정보가 포함되면 안됩니다. 하지만 verify signature는 SECRET KEY를 알지 못하면 복호화할 수 없습니다. 

아래는 위 내용을 기반으로 인증하는 예시입니다.

사용자 A가 B의 데이터를 보고자 payload를 조작하려 합니다. payload에 있는 A의 ID를 B의 ID로 변경해 인코딩한 후, 서버에 해당 토큰을 전달합니다. 그러면 서버는 처음 암호화된 verify signature를 검사하게 됩니다. 여기서 payload는 B사용자의 정보가 들어있으나, verify signature는 A의 payload를 기반으로 암호화되어 있기 때문에 유효하지 않는 토큰으로 간주합니다. 따라서 A의 사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없습니다.

JWT의 인증 방식은 아래의 단계와 같습니다.

  1. 클라이언트가 로그인을 위해 해당 정보를 서버에 전달
  2. 서버에서는 전달된 데이터로 사용자를 확인하고 사용자의 고유한 ID값을 부여한 후, 기타 필요한 정보와 함께 Payload에 추가
  3. JWT 토큰의 유효기간을 설정
  4. 암호화할 SECRET KEY를 이용해  Access Token을 발급
  5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 추가하여 전달

  6. 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작 여부, 유효기간을 확인

  7. 해당 토큰이 유효하면 Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 호출

세션/쿠키 방식과 가장 큰 차이점은 세션/쿠키는 세션 저장소에 유저의 정보를 넣는 반면, JWT는 토큰 안에 유저의 정보를 넣는다는 점입니다. 사용자 입장에서는 헤더에 세션ID나 토큰을 실어서 보내준다는 점에서는 동일하나, 서버 측에서는 인증을 위해 암호화를 하냐, 별도의 저장소를 이용하냐는 차이가 발생합니다.

장점

1. JWT는 발급한 후 토큰 검증만 하면 되기 때문에 추가 저장소가 필요 없음

2. 서버를 확장하거나 유지,보수하는데 유리

3. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능

  • 예를 들어 Facebook 로그인, Google 로그인 등은 모두 토큰을 기반으로 인증. 선택적으로 이름이나 이메일 등을 받을 수 있음

단점

1. 세션/쿠키의 경우 세션ID가 변질되었다고 판단되면 해당하는 세션을 지우면 되지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능하므로 유효기간이 지나기 전까지  정보들을 탈취할 수 있음

  • 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급하면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있음

2. payload 정보가 제한적 Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있어서 담는 데이터가 제한적임 

3. 세션/쿠키 방식에 비해 JWT의 길이가 기므로 인증이 필요한 요청이 많아질수록 서버의 자원낭비가 발생

4. 단점 1번과 연관된 문제로 유효기간을 짧게 하면 재로그인 시도가 잦아지고 길면 해커에게 탈취될 가능성이 큼

Refresh Token

Access Token(JWT)를 통한 인증 방식의 문제로 탈취당할 경우 보안에 취약하다는 점입니다. 유효기간이 짧은 Token의 경우, 사용자는 새 Token을 발급받기 위해 로그인을 자주 시도해야 하고 유효기간을 늘리면, 탈취당했을 때 보안에 취약해지게 됩니다. '유효기간을 짧게 하면서 보안을 챙길 수 있는' 물음에 나온 방식이 Refresh Token입니다.

Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 로그인이 완료됐을 때, Access Token과 동시에 Refresh Token은 긴 유효기간을 갖고 발행되고 Access Token의 유효기간이 만료되었을 때 새로 Token을 발급해주는 열쇠가 됩니다. 

예를 들어, Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간으로 설정하게 되면 사용자에게 발급된 Token이 1시간이 지나게 되면 Access Token은 만료되지만 Refresh Token의 유효기간은 아직 남아 있기 때문에 사용자의 재로그인 없이 Access Token을 새롭게 발급받을 수 있습니다. 

위의 예제와 같이 Refresh Token의 유효기간이 만료되면 사용자는 새로 로그인해야 하고 Access Token과 동일하게 Refresh Token도 탈취될 가능성이 있기 때문에 적절한 유효기간 설정이 필요합니다.

 

아래는 Refresh Token이 포함된 JWT 인증 방식입니다. (1~3번은 JWT와 동일)

  1. 클라이언트가 로그인을 위해 해당 정보를 서버에 전달
  2. 서버에서는 전달된 데이터로 사용자를 확인하고 사용자의 고유한 ID값을 부여한 후, 기타 필요한 정보와 함께 Payload에 추가
  3. JWT 토큰의 유효기간을 설정
  4. 암호화할 SECRET KEY를 이용해  Access Token, Refresh Token을 발급 (일반적으로 회원 DB에 Refresh Token을 저장)
  5. 사용자는 Refresh Token을 안전한 저장소에 저장
  6. 사용자는 Access Token을 받아 저장(쿠키)한 후, 인증이 필요한 요청마다 토큰을 헤더에 추가하여 전달

  7. 시간이 지나 Access Token이 만료되었다고 가정

  8. 사용자는 6번과 같은 방식으로 Access Token을 헤더에 담아 서버에 요청

  9. 서버는 Access Token이 만료된 것을 확인하고 사용자에게 만료 메세지를 반환

    1. 사용자는 Access Token의 payload를 통해 유효기간을 알 수 있으므로 요청 이전에 바로 재발급 요청을 할 수 있음

  10. 사용자는 Refresh Token과 Access Token을 헤더에 담아 서버에 요청

  11. 서버는 요청받은 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작 여부, 유효기간을 확인하고 전달받은 Refresh Token과 저장해둔 Refresh Token을 비교. Token이 동일하고 유효기간이 지나지 않았다면 Access Token 발급

  12. 사용자는 새로 발급받은 Access Token을 저장하여 인증이 필요한 요청마다 토큰을 헤더에 추가하여 전달

장점

1. Access Token이 있을 때보다 안전

단점

1. 구현이 복잡

2. Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청이 잦음

'보안 & 보안' 카테고리의 다른 글

서버 인증 (JWT)  (0) 2019.08.17
서버 인증 (세션/쿠키 기반)  (0) 2019.08.17
SSL Pinning  (0) 2018.12.18
TLS (Transport Layer Security)  (0) 2018.12.18
스니핑  (0) 2017.04.12
패스워드 크래킹  (0) 2017.03.28

+ Random Posts