본문 바로가기

개발 공통 기술

JWT 는 만능이 아니다.

이 포스팅은 기본적인 JWT 이해가 있다고 생각하고 글을 작성하였습니다.

 

우리는 JWT 를 사용할때, JWT의 장점만 생각하고 단점에 대해 크게 고민없이 JWT를 사용하곤 한다.

모든 기술에는 장단점이 존재하며, JWT 에도 단점이 있다.

JWT 의 구조

JWT는 HEADER.PAYLOAD.SIGNATURE 이렇게 3가지의 필드가 존재한다. 모든 필드는 JSON으로 표현되며 이러한 JSON 필드들을 각각 Base64 인코딩하고 (.)을 기준으로 하나의 값으로 합친 것이 JWT 이다.

 


JWT 공격 방법

JWT는 Base64로 encoded JSON 값이며, Base64를 Decode하면 누구나 내용을 볼 수 있기 때문에 JWT 안에는 중요한 정보는 없어야 한다.

JWT Signature Secret Crack

JWT는 Signature를 Secret Key를 통해 생성하는데, 이 Secret Key 가 노출되거나 단순한 패턴을 사용하는 경우 여러가지 도구들을 이용해서 Crack을 시도하고 만약 공격자가 Crack을 통해 Secret 값을 얻어낸다면 임의로 JWT 토큰을 생성/수정할 수 있어 서비스 신뢰도에 크게 문제가 생길수 있다.

None Algorithm attack

JWT는 HEADER 영역에 alg 값을 통해 해싱 알고리즘을 명시할 수 있는데, JWT 토큰 생성 시 alg 값을 None으로 명시했거나, 일부 JWT 라이브러리들은 alg 값에 none 등 비 서명 처리 시 서명검증을 하지 않고 넘어갈 수 있는 취약성을 가지고 있다. 이를 이용하면 공격자가 Secret 값을 몰라도 이를 통해서 JWT를 임의로 생성하거나 수정할 수 있다.

JKU and X5U

jku, x5u는 JWS(Json Web Signature)에서 사용되는 파라미터로 키 기반 서명을 위한 재료입니다. jku와 x5u 필드를 통해 키 검증을 위한 서버를 지정할 수 있으며 이 값들은 HEADER 필드에 명시한다.

 

  • JKU header parameter: JKU는 “JWK Set URL”의 약자로 토큰검증에 사용되는 키 집합을 가리키는 URL을 명시하는데 사용되는 선택적 헤더 필드입니다. 공격자가 자체 키 파일을 호스팅하고 애플리케이션이 이를 사용하여 토큰을 확인하도록 지정할 수 있습니다.
  • JWK header parameter: JWK(JSON Web Key) 헤더 매개변수를 사용하면 공격자가 토큰을 확인하는 데 사용되는 키를 토큰에 직접 포함할 수 있습니다.
  • X5U, X5C URL manipulation: JKU 및 JWK 헤더와 유사한 X5U 및 X5C 헤더 매개변수를 사용하면 공격자가 토큰을 검증하는 데 사용되는 공개 키 인증서 또는 인증서 체인을 지정할 수 있습니다.
{
  "typ": "JWT",
  "alg": "HS256",
  "jku": "https://manipulationsite.com",
  "x5u": "https://manipulationsite.com"
}

jku 와 x5u에 명시된 도메인은 이를 서명 검증하는 Application에서 접근해서 검증할 Trusted 서버이기 떄문에 만약 이 값이 변조되었다면 서버는 변조된 서버로 서명 검증을 하려고 요청하게 된다. (JWT 검증을 엉뚱한 곳에가서 하게 만드는 것)

 

이 때 jku와 x5u 도메인에 대한 검증이 과정이 있을 가능성이 존재하지만, 이 또한 SSRF와 유사하게 도메인 검사 로직을 우회하는 형태로 공격 페이로드 구성이 가능하다.

 

 


JWT 수비 방법

내부에 중요정보를 저장하지 않는다

JWT는 Base64로 인코딩된 JSON 형태의 값. Base64기 떄문에 쉽게 디코딩해서 볼 수 있고 당연히 중요한 정보는 JWT에 담지 않는 것이 원칙이다.

None Algorithm

JWT의 Signature에 사용되는 알고리즘은 특정한 알고리즘(에를들면 HS256)이 지정되어야 합니다. 만약 none 상태로 사용된다면 쉽게 크랙할 수 있습니다.

Signature Secret의 복잡도

JWT는 내부 값과 유효기간등이 포함된 데이터에 대한 서명을 포함하고 있다. 이는 서버에서 발급 시 지정한 알고리즘에 따라서 서명 후 JWT 토큰에 포함되며 서버에서 유효기간과 함께 서명의 정상 여부를 검증하게 된다. 한번 깨진 secret은 서비스 전체의 JWT 토큰들에 영향을 끼칠 수 있기 떄문에 굉장히 잘 관리되어야 한다.

짧은 만료 시간

물론 쿠키나 세션도 회사마다 만료시간을 두어 강제로 invalidate 하게 두는 경우도 있기는 하다. JWT도 탈취되었을 경우의 안정성 때문에 만료시간을 짧게 가져가기도 한다.

30분 혹은 1시간 정도의 짧은 토큰을 저장한다면, 만약 토큰이 탈취되어서 서버가 이를 강제로 끊지는 못하겠지만 짧은 시간만 유효하기 때문에 최소한의 보안성을 보장 할 수 있다.

하지만, 사용자 입장에서는 30분, 1시간만 유효하기 때문에 1시간 뒤에는 ID, PW를 또 입력해야 하는 불편한 경험을 느낄 수밖에 없는데, 짧은 유효시간을 보완할 만한 두 가지 방법이 있다.

짧은 JWT 유효시간을 보완하는 방법들

서버가 JWT의 만료되기 직전의 유효시간을 보고 재발급을 어떤 방식으로 하느냐에 따라서 갈린다. (대표적인 2가지 방법)

1. Sliding Session

특정한 서비스를 계속 사용하고 있는 특정 유저에 대해 만료 시간을 연장시켜주는 방법.

어떤 글을 지속적으로 쓰고 있는 유저가 짧은 만료시간 때문에 글을 쓰다가 인증이 취소되어 글이 날아가면 아까 이야기한 불편한 경험을 제공하게 될 것입니다. Sliding Session을 사용하게 된다면, 글쓰기, 결제 등과 같은 특정 action을 유저가 행동하였을 때 새롭게 만료시간을 늘린 JWT를 다시 제공함으로써 만료시간을 연장하여 보완하는 방법.

다만, 접속이 단발성으로 일어난다면 Sliding Session으로 연장시켜줄 수 없는 상황이 생기고, 너무 긴 Access Token을 발급시켜준 상황이라면 Sliding Session 때문에 무한정 사용하는 상황이 발생할 수 있다.

function updateJWT(){
  // JWT를 갱신하는 함수를 하나 만들고...
  // 물론 이는 서버로 요청해서 받아와야겠죠.
  // 단 너무 잦은 요청이 부담스럽다면 현재 JWT의 유효시간을 보고 갱신해도 됩니다.
  // 예를들면.. 만기가 다와갈 때
}

// window 전체에 onmouseenter를 걸어서 updateJWT가 호출되도록 합니다.
// 그러면 사용자가 마우스 액션이 있을 때 JWT를 자동으로 갱신 시켜줄 수 있습니다.
// 비슷하게 키보드도 있겠네요.
window.addEventListener("mouseenter", updateJWT, false);

2. Refresh Token

가장 많이 사용하는 방법으로 JWT를 처음 발급할 때 Access Token과 함께 Refresh Token이라는 토큰을 발급하여 짧은 만료시간을 해결하는 방법입니다.

짧은 시간이 아닌 비교적 긴 시간 (7일, 30일 등)의 만료시간을 가진 Refresh Token은 말 그대로 Access Token을 Refresh 해주는 것을 보장하는 토큰입니다. 만약 클라이언트가 Access Token이 만료됨을 본인이 인지하거나, 서버로부터 만료됨을 확인받았다면 Refresh Token으로 서버에게 새로운 Access Token을 발급하도록 요청하여 발급받는 방식입니다.

 

 

 

 

'개발 공통 기술' 카테고리의 다른 글

직렬화(Serializable)란 무엇인가?  (0) 2023.01.15