JWT 해킹 사례를 통한 취약점 분석하기

반응형

많은 웹서비스들이 JWT(JSON Web Tokens)를 쿠키에 저장해놓고 사용자 인증에 사용하고 있습니다.
먼저 JWT가 무엇인지 알아보고, JWT의 취약점에 대해서 설명합니다.
_

JWT란?

JWT는 서버와 클라이언트 간의 데이터를 안전하게 보관하고 처리하기 위한 인터넷 표준 RFC 7519 입니다. 그리고 JWT는 header, payload, signature를 base64 인코딩한 문자열입니다. 토큰에 포함된 내용들은 암호화되어 있지 않습니다. 그래서 누구나 확인 할 수 있습니다.
JWT 소개는 velopert님 블로그에서 아주 자세하게 설명하고 있어서 아래 링크로 대신합니다.

_

JWT 서명 원리

JWT는 기본적으로 HS256 알고리즘을 가장 많이 사용합니다. HS256는 대칭키를 SHA-256로 해시한 HMAC으로 payload를 서명했음을 의미합니다. HS256는 서명과 검증에 하나의 키만 사용하므로 서명과 검증 역할을 서버에서 합니다. 이때 서명에 사용되는 secretKey는 매우 강력해야 하며, 그리고 가장 안전하게 보관되어야 합니다.
아래에서 JWT 서명과 검증 원리를 설명합니다.
우선 header와 jwt에 포함할 payload를 준비합니다.

var header = {
  alg: "HS256",
  typ: "JWT"
}

var payload = {
  name: "kcoding",
}

그 다음 header와 payload를 base64로 인코딩합니다. 그리고 두 문자열을 연결하여 하나의 문자열로 만듭니다.

var encodedHeader = Buffer.from(JSON.stringify(header), 'binary').toString('base64');
var encodedPayload = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64');
var securedInput = `${encodedHeader}.${encodedPayload}`;

이제 HS256 알고리즘으로 서명을 합니다. 서명 역시 base64로 인코딩합니다.

if(header.alg === 'HS256') {
  var hmac = crypto.createHmac('sha256', secretKey); // HMAC
  hmac.update(securedInput); // SIGN
  var signature = hmac.digest('base64'); // base64
}

이제 마지막으로 header, payload, signature를 모두 연결하여 하나의 문자열로 만듭니다. 마지막으로 불필요한 문자는 제거합니다. 이 문자열이 JWT가 됩니다.

var jwt = `${securedInput}.${signature}`
jwt = jwt.replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');

검증 과정은 매우 단순합니다. payload가 변조되었는지만 판단하면 되므로, 위에서 설명한 서명 과정을 동일하게 수행하고 signature를 서로 비교하여 유효한 서명인지를 확인합니다.
_

JWT 를 무효화하는 방법 소개

None 알고리즘 공격

JWT 해더에는 서명에 사용된 알고리즘이 정의되어 있습니다. JWT 헤더에 정의된 알고리즘 유형을 "alg": "none"로 변경하고 서명을 빈값으로 변경합니다. 이러한 방법으로 변조된 JWT를 서버에서 유효하게 인식하도록 하는 방법입니다.
참고로 jwt.io에서 권장하는 JWT 라이브러리는 해당 취약점이 모두 패치되었습니다.
예를 들어 다음 토큰은 유효한 것으로 간주됩니다.

eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.

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

{
"user" : "admin"
}

아래 프로그램을 사용하면 alg 위변조 공격을 쉽게 테스트 해볼 수 있습니다.

해당 테스트를 하기 위해서 Burp Suite를 설치합니다.
Burp Suite는 웹 프록시 프로그램입니다. 웹브라우저만으로는 통신간의 정보를 자세히 보기가 힘듭니다. 하지만 프록시 프로그램을 사용하면 클라이언트의 요청정보, 서버의 응답정보를 상세히 확인할 수 있습니다. 또한 서버로 전송되는 정보를 변경하여 취약점 분석에도 사용할 수 있습니다.
json-web-tokens는 코드를 직접 다운받아서 빌드해서 사용해도 되고, BApp Store에서 다운로드 받아서 사용해도 됩니다.
None 알고리즘 공격 테스트를 확인하기 위해 "alg": "none"로 JWT의 헤더를 업데이트하고 빈 서명을 제공하면 됩니다. 해당 공격에 취약한 시스템의 경우에는 데이터가 허용되며 원하는대로 페이로드를 자유롭게 수정하고 위변조 할 수 있습니다.
아래 스크린 샷에서 볼 수 있듯이 Burp Suite의 Repeater에서 "json-web-tokens" Extender을 사용하여 테스트를 수행할 수 있습니다.

무차별 대입 공격(brute-force attack)

JWT를 무력화할 수 있는 방법은 여러가지가 있지만, 가장 강력하면서 단순한 방법이 있습니다. 바로 무차별 대입 공격(brute-force attack)으로 대칭키와 서명을 비교하여 JWT를 무효화 시킬 수 있습니다. 만약 서명과 일치하는 대칭키를 발견한다면 우리는 이제 JWT를 위조할 수 있는 것입니다.
참고로 이 작업을 수행할 수 있는 공개된 프로젝트가 몇 가지 있습니다.

여기서 c-jwt-cracker를 사용하여 테스트 해보겠습니다. 아래는 테스트에 사용한 맥북 사양입니다.

  • 프로세서 2.7 GHz 인텔 코어 i5
  • 메모리 8GB 1867 MHz DDR3
  • 그래픽 인텔 아이리스 그래픽 6100 1536 MB

_
openssl가 설치되어 있지 않다면, 먼저 openssl를 설치합니다.

$ brew install openssl

그 다음 터미널에서 다음 명령을 실행합니다.

$ make OPENSSL=/usr/local/opt/openssl/include OPENSSL_LIB=-L/usr/local/opt/openssl/lib

혹시 맥이 아닌 우분투라면 아래와 같이 설치합니다.

$ apt-get install libssl-dev

그 다음 jwt-cracker 리포토리를 clone 합니다.

$ git clone https://github.com/brendan-rius/c-jwt-cracker.git
$ cd c-jwt-cracker

이제 jwt-cracker를 실행 해봅니다.
다음은 sha256 해시함수를 사용하도록 하고 최대 암호 길이는 5글자, 그리고 문자열은 ABCSNFabcsnf1234 로 제한하여 실행하였습니다.

time \
  ./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.cAOIAifu3fykvhkHpbuhbvtH807-Z2rI1FS3vX1XMjE \
  ABCSNFabcsnf1234 5 sha256

⇒ 결과적으로 Secret Key는 Sn1f이며 제 맥북에서 알아내는데 1초도 걸리지 않습니다.
Secret Key에 사용된 문자열과 길이, 알고리즘을 예상할 수 있다면 쉽게 알아낼 수가 있습니다.
_

결론

허가 받지 않은 시스템에서 위 방법으로 해킹을 시도하는 것은 불법입니다. 하지만 보안 취약점을 발견한다면 반드시 서비스 업체에 알려주세요. 더 안전한 서비스를 만드는 데 많은 도움이 됩니다.
우리는 인증 토큰을 어떻게 하면 안전하게 관리할 수 있을지 항상 고민해야 합니다.
최근 크롬 브라우저에 새로 업데이트된 Trust Tokens를 사용하는 것도 토큰 탈취에 대해 보안을 높일 수 있는 하나의 방법이 될 것입니다. 사실 저도 아직 Trust Tokens 기능을 사용해보진 못했습니다.
Trust Tokens에 대해서 궁금하신 분은 아래 글을 읽어보세요.

_

TMI 1
몇 달전에는 해커가 EA 슬랙 로그인 토큰(쿠키)을 해킹하여 소스코드를 탈취한 사건이 있었죠. 토큰에 만료기간이 없거나 너무 길다면 발생할 수 있는 시나리오입니다. JWT를 생성할 때는 유효시간은 최대한 짧게 하세요.

_

TMI 2
그리고 아래 사이트에서 내 비밀번호를 해킹하는데 얼마나 걸리는지 예측해볼 수 있습니다. JWT 서명에 사용하는 비밀키는 항상 강력하게 생성하세요.

https://howsecureismypassword.net/

_

이 글을 작성하기 위해 참고 했던 자료

반응형