요즘 인증 관련해서 oAuth를 많이 사용하면서 자연스레 JWT에 대한 이야기도 많습니다.
테스트를 하다보니 개념만 알고있던 JWT에 대해 공부도 되고 보안적인 문제점이 있을까 생각하는 계기가 되어 글 작성해봅니다.
JWT란?
이름 그대로 JSON을 이용한 Web Token 입니다. 주로 서비스에 대한 인증이나 CSRF 토큰등에 사용될 수 있겠지요. 이런 JWT는 아래와 같은 구조를 가집니다.Header + Payload + Signature 로 구성됩니다.
https://cask.scotch.io/2014/11/json-web-token-overview1.png |
헤더와 페이로드로 나눌 수 있으며 각각 토큰에 대한 큰 정보와 실제 데이터로 구성이 됩니다.
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 자체적인 문제도 있을겁니다.
(이부분은 저도 풀어나가려고 시간을 많이 투자했었고, 다른 분들도 JWT 자체에 대한 문제를 많이 제기하죠.)
1.토큰 내 중요한 정보 노출
일단 가장 흔한 경우는 이 토큰을 만들기 위해 사용되는 데이터들입니다.
토큰이기 때문에 각 계정이나 세션을 의미하는 고유한 데이터도 포함될 수 있고 개인정보도 포함될수도 있습니다. 보안 분석가라면 이 부분은 꼭 체크해주시고, 개발자라면 중요한 정보는 사용되지 않도록 구성해야 할 것입니다.
2.토큰 내 값 조작으로 인가되지 않은 접근 권한 획득(Signature에 대한 해법이 필요)
이 방법은 토큰 내 권한이나 인가에 관련된 값을 변조하여 공격을 수행할 수 있습니다.
단순하게 토큰에 의존하여 사용자를 식별한다면 공격자는 토큰을 위조하여 서버를 속일수도 있죠.
아래 샘플은 jwt 공식 홈(jwt.io) 에서 제공되는 기본 sample 코드인데요.
여기서 보아도 payload 데이터에 admin을 의미하는 값이 들어있습니다.
이러한 부분들을 조작하여 전송할 때 서버가 별다른 검증없이 클라이언트 정보를 신뢰한다면 인가받지 않은 부분에 접근할 수 있게 될 수 있습니다.
추가로 jwt 홈페이지에 가면 쉽게 만들어보고 테스트할 수 있습니다.
물론 직접짜서 해보는게 더 재미있긴 하지만요 ㅎㅎ
(https://jwt.io/)
HAHWULSecurity engineer, Gopher and H4cker! |
결국 시크릿코드를 알아야 가능한거아닌가요?
ReplyDelete답글이 좀 늦었네요. 만약 변조된 요청을 위해서는 시크릿코드가 필요하게됩니다. 단순이 암호화하는 키가 아니라 서명에 들어가는 값이구요.
Delete제가 대충 만들었던 아래 jwt 토큰도 https://jwt.io/ 에서 풀어보시면 Invalid Signature가 발생하긴 하지만 만들때 넣었던 param 값은 보이게 됩니다. # payload = {:param1 => 8080} 부분
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJhbTEiOjgwODB9.wNDKI_bZi5E5uWFwQ72QbMUEsn0_mlyT1F2y_0xWSKA
요약하자면 시크릿코드가 있으면 정상 서명된 요청으로 위조할 수 있고, 없다면 토큰 내부 데이터 정도 볼 수 있습니다.
다만 개발자분들 중 일부가 중요정보를 토큰에 넣어 사용하기에 위험할 수 있는거죠.
이해에 도움되셨길 바래요 : )