jwt 딥한 내용 훑어보기용 따라쓰기

이종호·2021년 4월 11일
0

보안

목록 보기
2/2
post-thumbnail

https://code-machina.github.io/2019/09/01/Security-On-JSON-Web-Token.html

이 페이지는 해당 블로그 내용을 따라 치면서 개념을 쭉 훑어보기 위해 사용한 공간입니다.
문제가 된다면 즉시 삭제하겠습니다.


JSON 웹 토큰(JWT) 보안

개요

이 글을 통해 JWT란 무엇이요, 어떠한 특성을 가지고 있는지 정리하며 어떠헥 활용되는지 정리한 뒤에 보안 위험성을 식별하고 위험을 방지하는 전략을 소개해 드리도록 하겠습니다.

JWT란?

RFC-7519를 통해 JWT는 상호간의 정보를 안전하게 전송하는데 사용되는 기술입니다.
JSON객체를 이용하며 압축적이고 스스로 무결함을 입증할 수 있는 방식이므로 효율성이 높습니다.
즉, 디지털 서명을 통해 검증가능하고 신뢰할 수 있는 정보를 운반합니다.
JWT는 비밀키를 ㅏ용하여 서명되는데 이 때 HMAC 알고리즘을 사용합니다.
또한 RSA를 이용한 공개키/비밀키 쌍을 이용할 수 있습니다.

JWT는 주로 신원(identity) 정보와 클라이언트의 요청과 관련된 정보를 운반하는데 사용됩니다.
이 컨테이너는 서버에 의해서 서명되며 클라이언트의 조작을 사전에 방지합니다.

예를 들어 일반 사용자 역할을 관리자 역할로 승격시키거나 로그인 상태를 변경을 시도할 경우 이를 차단할 수 있습니다.(보안적 기능)

이 토큰은 인증(aythentication)을 통해서 생성됩니다. 그리고 서버에 의해 검증됩니다.
어플리케이션에 의해 아이디 카드(컨테이너라고 불러도 무방)처럼 서버에게 제출할 수 있으며 서버는 무결성(integrity)와 유효성(validity)를 안전한 방식으로 검증할 수 있습니다.

이 모든 과정이 휴대성(portability)과 비상태성(stateless) 상태로 이루어지는 JWT속성으로 인해 클라이언트와 서버 기술의 분리가 용이합니다.

JWT의 구조

JWT.IO에 따르면, 토큰은 아래와 같은 구조를 갖추고 있습니다.
3개의 파트로 분리되며, 전송을 위해 base64를 통해 인코딩합니다.

JWT 고려사항

JWT토큰이 사용하기 쉽고 stateless한 방법으로 서비스를 노출 시킬 수 있지만, 모든 경우에 있어 해결책은 아닙니다.
만약에 어플리케이션이 완전히 stateless한 상태를 유지할 필요가 없다면 여러부능ㄴ 전통적인 session시스템을 통해 여러분의 웹 프레임워크를 구성할 수 있습니다.

None 해싱 알고리즘

이 경우는 공격자(attacker)가 해싱 알고리즘을 none으로 변경하는 것을 의미합니다.
몇몇 라이브러리들은 none알고리즘으로 서명된 토큰을 유효한 토큰으로 인식하는 경우가 있습니다.
따라서 공격자는 토큰의 요청(claim)을 변조하고 이는 어플리케이션에 의해서 신뢰되는 경우 입니다.

서버 측에서 RSA알고리즘을 통해서 다음의 구조가 성립한다고 가정해봅니다.
비동기키(asymmetic)의 특성상 Public키를 통해서 암호화를 하고 서버에서 Private키를 통해서 복호화를 하는 구성을 택하게 됩니다.

그러나, 만약 공격자가 이 알고리즘을 HMAC으로 전환할 경우 Public Key를 통해서 복호화와 암호화를 하게 됩니다.(서버특은 암호화를 Public Key로 하기로 로직이 구성된 상태이므로).
따라서 공격자는 알고리즘을 바꿈으로써 자신에게 교부된 Public 키를 통해 JWT를 재서명함으로써 권한 상승
을 할 수 있습니다.

  1. 서버 ===== Public 키 교부 =====> 클라이언트
  2. 서버 <===== Token (Public 키 암호화 서명, HMAC, 데이터 변조) ===== 클라이언트
  3. 서버 ===== Grant Admin Privs =====> 클라리언트

예방책(mitigation)

JWT라이브러리 사용 시 아래의 라이브러리에 대해 주의를 기울여야 합니다.

  • node-jsonwebtoken
  • pyjwt
  • namshi/jose
  • php-jwt
  • jsjwt

구현 샘플

아래의 샘플에서 알고리즘 사용에 대해 인가되지 않은 제어를 막기위해 명시적으로 HMAC-256해싱 알고리즘을 사용을 선언합니다.
그리고 토큰에 대해 검증 컨텍스트를 생성하여 토큰의 유효성 검증에 사용합니다.

// JVM 메모리 상에 문자열로 키 정보를 담는 컨테이너 생성
// transient 키워드를 통해 직렬화를 차단
private transient byte[] keyHMAC = ...;

...

// 요청한 토큰에 대해 유효성 검증 컨텍스트를 생성
// 명시적으로 HMAC-256 해싱 알고리즘 사용을 선언한다.
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(keyHMAC)).build();

// 생성한 컨텍스트를 바탕으로 토큰을 검증하고 만약 검증이 실패한다면 예외가 발생한다.
DecodedJWT decodedToken = verifier.verify(token)

토큰 하이재킹

공격자에 의해서 토큰이 중간에서 가로채기를 당하거나 도난을 당하였을 경우 시스템에 대한 접근 권한을 얻기 위해 악용될 수 있습니다.

방어 방법

이를 효과적으로 방어하기 위해서는 사용자 컨텍스트를 토큰에 생성해야 합니다.
사용자 컨텍스트 정보는 다음의 정보로 구성됨에 유의해야합니다.

  • 인증 시퀀스 동안에 생성된 무작위 문자열, 그리고 이 문자열을 토큰 내에 포함되어야 합니다. 또한, 이 정보가 클라이언트에 전송될 때(HttpOnly, Secure, SameSite, cookie prefixes와 같은 보안 설정이 갖추어 져야 합니다.)

  • 무작위 문자열의 SHA256 해시는 토큰 내에 저장되며 XSS이슈로 인해 공격자가 임의의 문자열값을 읽거나 예상되는 쿠키를 설정하는 것을 방지할 수 있습니다.

아래는 위에서 JWT를 보호하기 위한 HTTP헤더 플래그를 설명한 내용입니다.(이 부분은 매우 중요하며 면접에도 자주 나오는 단골 개념들이니 꼭 유념하시길 바랍니다.)

Secure 쿠키는 HTTPS 프로토콜을 통한 암호 요청만을 통해서 전송할 수 있습니다. 예를 들어 http 통신을 하는 안전하지 않은 사이트는 Secure 디렉티브로 설정된 쿠키를 구울 수 없습니다.

HttpOnly 쿠키는 자바스크립트의 Document cookie API를 통해 접근이 불가합니다. 이들은 오로지 서버로만 전송됩니다. 예를 들어 server-side세션은 Javascript가 접근할 필요가 없습니다. 따라서 Http Only 플래그가 반드시 설정되어야 합니다.

SameSite쿠키는 서버로 하여금 쿠기가 교차 도메인 전송이 차단합니다. CSRF에 대해 보안을 제공합니다. SameSite 쿠키는 상대적으로 신규 플래그이며 모든 주요 브라우저에 의해서 지원되는 사항입니다.

쿠키 접두사(Prefix) 는 브라우저에게 특정 속성(attribute, like Secure etc.)이 필요하다고 이양기 하는 역할을 합니다. __Secure-이 대표적인 예입니다. __Host- Prefix가 설정된 경우 Path=/Secure 속성이 모두 필요함을 브라우저에게 알려줍니다.

IP주소는 IP주소의 변경 이슈로 인해 사용할 수 없습니다. 또한, IP주소의 사용은 잠재적으로 유럽 GDPR 컴플라이언스 레밸에서 이슈를 야기할 가능성이 존재합니다.

토큰 유효성 검증 동안, 수신된 토큰인 올바른 컨텍스트를 포함하지 못한다면 다시 요청을 수신하도록 하거나 서버로부터 거절되는 구현을 선택해야 합니다.

코드는 오늘은 생략하겠습니다..

사용자에 의한 명시적 토큰 철회 / 취소

이 문제는 JWT에 고유한 문제입니다.
토큰이 만료될 떄 유효하지 않게 되며 사용자는 명시적으로 토큰의 유형성 검증을 취호살 수단이 없게 됩니다.
이는 즉 토큰이 유출될 경우 사용자는 토큰 자체를 취소할 수 ㅇ벗게 되며 공격자를 차단할 수 없음을 의미합니다.

예방 방법

이러한 위험으로부터 보호하는 방법은 token의 블랙리스트를 구현하여 logout기능을 모방하는데 사용하는 것입니다.
이는 전통적인 세션 시스템에 존재하는 방법입니다.

블랙리스트는 토큰의 헥사로 인코딩된 SHA256 다이제스트와 취소 일자를 함께 보관합니다.
이 기간동안 발급된 토큰의 기간 유효성보다 상위의 우선순위를 가집니다.

사용자가 logout을 원할 때 지정된 서비스가 호출되며 제공된 사용자 토큰을 블래길스트에 넣어둡니다. 그리고 나중에 어플리케이션에서 사용시 토큰을 말소시킵니다.

토큰 정보 노출

공격자가 토큰에 접근하여 정보를 추출하는 방식입니다.
이는 시스템에 대한 정보를 얻기 위한 방법입니다.
정보의 예는 보안 권한, 로그인 유형 등이 있습니다.

예방 방법

보호의 방법은 동기 알고리즘을 이용한 토큰을 암호화하는 것입니다.
그러나 이러한 공격 유형에는 Padding Oracle이 있습니다.
보안 목적을 모두 달성하기 위해서는 AES-GCM(Galois/Counter Mode)알고리즘을 사용합니다.
패딩 오라클이 궁금하다면 아래의 링크를 참조해주세요. Jupyter노트북을 통해 원리를 완벽히 분석해놓았습니다.

보호의 방법은 동기 알고리즘(AES등의 대칭키 알고리즘)을 이용하여 토큰을 암호화하는 것입니다.
그러나 AES중 CBC모드에는 Padding Oracle공격방법이 있습니다. 보안 목적을 모두 달성하기 위해서는 AES-GCM알고리즘을 사용합니다.

암호화는 내부 정보를 감추는 수다능로 사용됩니다. 그러나 JWT토큰을 변조하는데 있어 첫 번째 방어 수단은 시그니처 입니다.
토큰 시그니처와 유효성 검증은 언제나 준비외어 있어야 합니다.

구현 샘플

  • 토큰 암호화

    • Google Tink는 암호라이브러리이빈다. 암호화 작업을 처리할 때 사용합니다.
    • google Tink라이브러리에서 Privitives는 암호 작업을 말합니다.
      • Aead aead = AeadFactory.getPrimitive(keysetHandle);와 같이 사용됩니다.
      • 구글은 primitives를 가리켜 cryptographic tasks라고 펴현합니다.
      • 여기서 Primitives란 원초라는 의미로 해석이 됩니다.
      • 또한 프로그래밍 언어에서 Primitive Data Type이 있죠, int, string등 기본 제공 타입들입니다.
      • 즉, 암호화에 있어서 원초적이고 기본이 되는 것은 암호화 스펙입니다. 따라서, Primitive라는 단어를 사용한 것이 아닌가 생ㄱ가이 드네요.
  • Determinsistic AEAD Primitive 란
    Determinsistic AEAD는 DAEAD 라고 줄여 쓰면 그 의미는
    동일한 데이터를 암호화 한다면 항상 동일한 암호문을 출력한다.
    (Encrypying the same data always yields the same ciphertext)
    그리고 암호화된 데이터를 탐색하는 스키마나 키를 랩핑하는 용도에 유요합니다.

토큰의 약한 암호화 키(secret)

secret이 HMAC SHA256 알고리즘에 사용되고 이 해시 값이 token 시그니처에 사용되는 경우 만약 secret이 취약하다면 brutforcing의 위험에 노출될 수 있다.

예방 방법

screte의 상도(strength)를 높이는 방법은 Alphanumeric + special characters를 문자열 셋을 이용합니다.
s8cb!=&xi^%&!(ro51jhuuyxzf2o8ws3oz1n87


profile
코딩은 해봐야 아는 것

0개의 댓글