JWT(JSON Web Token) 인증방식과 보안테스팅, 취약점 분석

🚧 JWT 관련 테스팅, 보안 내용은 제 블로그 내 Cullinan 페이지에서 관리중입니다. Cullinan > JWT 항목을 참고해주세요.

요즘 인증 관련해서 OAuth가 많이 사용하면서 자연스레 JWT에 대한 이야기도 많아지고 있습니다. 테스트를 하다보니 개념만 알고있던 JWT에 대해 공부도 되고 보안적인 문제점이 있을까 생각하는 계기가 되어 글 작성해봅니다.

JWT란?

이름 그대로 JSON을 이용한 Web Token 입니다. 주로 서비스에 대한 인증이나 CSRF 토큰등에 사용될 수 있겠지요. 이런 JWT는 Base64(Header) + Base64(Payload) + Base64(Signature) 로 구성됩니다.

크게 헤더와 페이로드로 나눌 수 있으며 각각 토큰에 대한 큰 정보와 실제 데이터로 구성이 됩니다.

  • JWT에서 토큰은 헤더와 Payload로 나눠짐
  • Header: 암호화 알고리즘 및 Type을 의미함
    {
    "alg":"HS256",
    "typ":"JWT"
    }
    
  • Payload : 전송할 내용
    {
    "test":0000001,
    "User":"TestUser1",
    "auth":"nomal_user"
    }
    

헤더 내용 중 alg는 보안 알고리즘, typ는 type을 의미합니다. alg는 HS256 외 다수의 알고리즘을 지원합니다. 이렇게 지정한 데이터는 인코딩 과정을 거쳐 아래와 같은 Token으로 변환되어 사용됩니다.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJhbTEiOjgwODB9.wNDKI_bZi5E5uWFwQ72QbMUEsn0_mlyT1F2y_0xWSKA

  • Header: red
  • Payload: Blue
  • Signature: Yellow

JWT 테스트를 위한 세팅

저는 주로 루비와 C로 테스트를 하기 때문에.. Ruby 기준으로 작성하겠습니다. (물론 다른언어도 별 차이 없습니다 =_=)

인증은 항상 풀어보는게 가장 좋은방법이라 생각됩니다.(풀어진다면..) 토큰 자체에는 복잡한 규칙은 없어 생각보다 간단한 방법으로 풀이가 가능합니다.

Generate JWT

hmac_secret = 'no'
payload = {:param1 => 8080}
token = JWT.encode payload, hmac_secret, 'HS256'    #encode(payload,key,algorithm)
puts "Encode Token: ", token

위 코드를 보면 hmac_secret 는 key를 의미하며 payload의 데이터를 해당 키를 이용해 HS256으로 Encoding 합니다. 이러한 결과값은 아래와 같이 JWT Token 형태로 나타납니다.

Encode Token: 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJhbTEiOjgwODB9.wNDKI_bZi5E5uWFwQ72QbMUEsn0_mlyT1F2y_0xWSKA

Decode JWT

require 'jwt'
hmac_secret = 'no'
payload = {:param1 => 8080}
token = JWT.encode payload, hmac_secret, 'HS256'    #encode(payload,key,algorithm)
puts "Encode Token: ", token
puts "Decode Token: ", JWT.decode(token,"no")   #decode(token,Key)

아까 만들어진 부분을 포함하여 다시 그 Key를 가지고 복원하는 코드를 추가하였습니다. 간단하죠?

ruby jwtencode.rb
Encode Token: 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJhbTEiOjgwODB9.wNDKI_bZi5E5uWFwQ72QbMUEsn0_mlyT1F2y_0xWSKA
Decode Token: 
{"param1"=>8080}
{"typ"=>"JWT", "alg"=>"HS256"}

코드로는 이렇게 표현하지만 사실 더 간단하게 풀 수 있습니다. JWT는 각 구간별로 Base64로 인코딩되기 때문에 해당 부분 긁어서 보시면 바로 확인 가능합니다.

Header: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

  • {“typ”:”JWT”,”alg”:”HS256”} Payload: eyJwYXJhbTEiOjgwODB9.
  • {“param1”:8080} Signature: wNDKI_bZi5E5uWFwQ72QbMUEsn0_mlyT1F2y_0xWSKA

JWT 어떤 보안적인 문제점들이 있을까?

이런 인증 환경에선 어떤 문제들이 있을지 고민을 좀 해보았습니다. 여러가지 방법이 있을 것이고, JWT 자체적인 문제도 있을겁니다.

토큰 내 중요한 정보 노출

일단 가장 흔한 경우는 이 토큰을 만들기 위해 사용되는 데이터들입니다. 토큰이기 때문에 각 계정이나 세션을 의미하는 고유한 데이터도 포함될 수 있고 개인정보도 포함될수도 있습니다. 보안 분석가라면 이 부분은 꼭 체크해주시고, 개발자라면 중요한 정보는 사용되지 않도록 구성해야 할 것입니다.

Signatrue에 대한 검증과 우회

이 방법은 토큰 내 권한이나 인가에 관련된 값을 변조하여 공격을 수행할 수 있습니다. 단순하게 토큰에 의존하여 사용자를 식별한다면 공격자는 토큰을 위조하여 서버를 속일수도 있죠. 서버가 별다른 검증없이 클라이언트 정보를 신뢰한다면 인가받지 않은 부분에 접근할 수 있게 될 수 있습니다.

Expire

JWT를 토큰으로 사용하게 되면 또 하나의 문제점이 발생합니다. 클라이언트단에서만 관리할 수 있기 떄문에 명시적 만료(로그아웃 시 기존 쿠키를 폐기)가 어렵습니다.

그래서 발급 시 적당한 시간의 Expire를 가져야하며, 어려운 경우 명시적 만료를 비슷하게 나마 구현해야합니다.

More

이 글 작성 당시 무지했던 부분이 좀 많았습니다. 공격자 관점, 방어자 관점에서 필요한 부분은 아래에 별도로 정리해두었으니 참고하시길 바랍니다 :D

  • Offensive: https://www.hahwul.com/cullinan/jwt/#-offensive-techniques
  • Defensive: https://www.hahwul.com/cullinan/jwt/#-defensive-techniques