jwt토큰 디코딩하면 페이로드 값을 다 아는데 왜 서명을 하는걸까? 본문

백앤드 개발일지/웹, 백앤드

jwt토큰 디코딩하면 페이로드 값을 다 아는데 왜 서명을 하는걸까?

giron 2022. 9. 1. 01:11
728x90

jwt 토큰이란

JWT(JSON Web Token)는 당사자 간에 정보를 JSON 객체로 안전하게 전송하기 위한 토큰이다. 이 정보는 디지털 서명되어 있으므로 확인하고 신뢰할 수 있습니다. JWT는 HMAC 알고리즘을 사용하거나 RSA 또는 ECDSA 를 사용하는 공개/개인 키 쌍을 사용하여 서명할 수 있습니다.

한가지 오해

처음에 jwt를 공부할 때, 어차피 디코딩하면 헤더에서 토큰 타입이나 알고리즘을 알 수있는데 왜 서명을 하는가였다. 그이유를 설명하자면 jwt의 서명의 목적을 생각해야한다.

디지털 서명되어 신뢰할 수 있는 이유는?(서명하는 이유)

JWT 토큰은 자체적으로 토큰 유효성 검사가 가능하다! 즉, HMAC또는 RSA와 같은 공개 또는 비대칭키를 통해 서명하기 때문이다. 단순히 JWT토큰을 받아서 해당 토큰이 위변조되었는지만 확인하여 위변조가 되지 않으면 JWT토큰 내부에서 사용자 정보를 꺼내 사용할 수 있기 때문이다. 바로 이렇게 토큰의 위변조가 있었는지 혹은 위변조를 방지하는 기법 중 하나를 HMAC(Hash-based Message Authentication)이라고 한다.

HMAC 알고리즘이란

MAC(Message Authenticate Code)?

원본 메시지와 전달된 메시지를 비교하여 변조 여부를 확인하는 방식이 MAC이다.

 

이때 Hash와 MAC을 사용하여 변조 여부를 확인하는 방식이 HMAC이다.

MD5나 SHA1, SHA256 같은 암호화 해시 함수를 사용하여 원본 메시지를 암호화한다. 이때, 일반 해싱 알고리즘과의 차이점은 HMAC은 송수진자가 미리 해시 암호 키를 나눠 가지고 있다는 것이다. 즉, 대칭키 알고리즘 방식이다.

예시

  1. 사전에 Sender와 Receiver는 사용할 공유 키(Secret key)를 공유한다. 그리고, 양쪽에서 사용할 해시 알고리즘을 정한다.
  2. Sender는 공유키를 사용해서 UserId를 해시한다.
  3. Sender는 원본 UserId와 그 해시결과(HMAC)을 쿼리스트링 값으로 Receiver에게 전달한다.
  4. Receiver는 받은 UserId를 공유키를 사용하여 같은 알고리즘으로 해시한 결과(Receiver's HMAC)를 만든다.
  5. Receiver가 만든 HMAC과 쿼리스트링으로 받은 HMAC이 같다면 UserId는 변경되지 않았다고 신뢰할 수 있다.

HMAC 동작방식

수도코드

1. secret key 길이 검사

  1. 입력받은 key 길이가 블록 사이즈보다 클 경우 hash 함수에 key 를 넣어서 키를 생성합니다.
  2. key 길이가 블록 사이즈보다 작을 경우 적은 길이만큼 '\0' 을 패딩해서 오른쪽을 채웁니다. 즉 key 길이가 40 이고 block size 가 64 일 경우 key 의 41번째 바이트부터 '0' 을 24개를 padding 합니다.

2. padded key 구현

  1. i_key_pad 값을 구하기 위해서 key 와 0x36 을 XOR 연산합니다.
  2. o_key_pad 값을 구하기 위해서 key 와 0x5c 를 XOR 연산합니다.
  3. 이제 구한 i_key_pad 값에 전송할 message 를 concatenation 한 후에 hash 를 돌리고 나온 값을 o_key_pad 에  concatenation 한후에 다시 hash 를 한 값이 HMAC 이 됩니다.
  4. hash(o_key_pad ∥ hash(i_key_pad ∥ message))
     
  5. 이제 송신자에게 HMAC 값과 message 를 서버에 전송하면 서버가 보유한 secret key 를 사용해서 전송받은 message 에서 HMAC 을 계산하고 client 가 전송한 HMAC 과 비교하면 됩니다.

*재연 공격(Reply attack)

서명처럼 무결성이 있는 암호화된 패킷을 공격하는 방법으로 패킷을 잡아두었다고 계속 그 패킷을 보내는 재연 공격이 있다. 이러한 재연 공격에 대해서는 난수 또는 TimeStamp로 감싸서 보내면 수신자가 받았을 때, 옛날 시간이면 재연공격으로 판단해서 방어하는 방법이 있다. 이러한 방법은 추후 OTP원리에도 적용된다.

JWT 구조

너무 흔히들 알고있어서 넘어가려고 했지만 조금만 언급하겠다.

크게 header, payload, signiture로 이루어져 있다.

header

token type과 서명 알고리즘(HMAC, RSA..)이 들어있다.

참고로 RSA는 타원곡선함수를 사용한 비대칭키 암호화로 키가 길어서 느리다는 단점이 있다.

{
  "alg": "HS256",
  "typ": "JWT"
}

payload

토큰에 담을 Claim 정보를 포함하고 있으며, Payload에 담는 정보의 한 조각을 Claim이라 부르고 이는 key/value pair로 이뤄져 있다. 여러 조각(claim)을 넣을 수 있다.

  1. 등록된 클레임
    1. iss: 토큰 발급자(issuer)
    2. sub: 토큰 제목(subject)
    3. exp: 토큰 만료 시간(expiration), NumericData 형식
    4. 와 같은 정해진 형식으로 데이터를 담는다.
  2. 공개 클레임
    1. 공개용 정보를 위해 사용된다. 충돌 방지를 위해 URI 포맷을 사용한다.
  3. 비공개 클레임
    1. 서버와 클라이언트 사이의 임의로 지정한 정보를 저장한다.

signiture

인코딩된 헤더와 인코딩된 페이로드 그리고 헤더에 지정된 알고리즘을 가져와 아래와같은 형식으로 만듭니다.(아래는 HMAC예시입니다.) 개인키로 서명된 토큰은 발급자도 확인할 수 있습니다.

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

JWT 토큰 주의점

HTTP 헤더를 통해 JWT 토큰을 보내는 경우 토큰이 너무 커지지 않도록 해야 합니다. 일부 서버는 8KB 이상의 헤더를 허용하지 않습니다. 따라서 토큰에 너무 많은 정보를 포함하려고 하면 안된다.

JWT는 언제 사용할까?

  1. 권한 부여
    1. 유저가 로그인할 때, jwt에 권한을 담아서 전달하면 유저는 어느 부분까지 접근이 가능한지에 대한 권한을 가질 수 있다.
  2. 정보 전달
    1. jwt는 헤더와 페이로가 서명이 된 토큰이므로 정보의 신뢰성이 있다. 또한 개인키로 암호화한 jwt는 송신자가 누구인지 알 수도 있다.

JSON 웹 토큰을 사용해야 하는 이유는 무엇입니까?

SWT(Simple Web Tokens)  SAML(Security Assertion Markup Language Tokens )과 비교할 때 JWT(JSON Web Tokens) 의 이점에 대해 이야기해 보겠다. 참고로 swt는 xml기반의 토큰이다.

JSON은 XML보다 덜 장황하기 때문에 인코딩될 때 크기도 작아져 JWT가 SAML보다 더 간결해집니다. 따라서 JWT는 XML 기반의 SAML 방식보다 크기가 작습니다. 또한 SWT방식은 HMAC알고리즘만 적용이 가능하다. JWT는 개인/공개키가 가능하므로 정보전달 할 때, 송신자 개인키로 암호화할 시에 송신자가 누구인지 알아 신뢰성 높게 사용할 수 있다. 또는 어떤 알고리즘을 사용했는지 범위가 넓어지므로 공격자가 공격하기 더 어렵게 한다.

정리

1. jwt를 사용하면 인코딩 됐을 때, 다른 토큰보다 덜 장황해서 크기가 작다.

2. HMAC뿐만 아니라 공개/개인키로 서명이 가능해서 공격자가 어떻게 서명했는지 알아차리기 더 어렵다.

728x90
Comments