JWT 디코딩

2024년 10월 8일 화요일

Today I Learned

날짜

2024년 10월 8일 화요일

내용

JWT 디코딩

네이버에선, 솔루션에 가입한 유저의 정보를 JWT 토큰으로 넘겨준다. 이 토큰을 각 솔루션에 제공되는 공개키를 이용해 디코딩하여 유저 정보를 저장해야 한다. 쇼피파이에서도 했던 거라 그리 안어려울 거라 생각했는데 난관에 많이 부딪혔다.

인식하지 못하는 알고리즘

JWT 토큰은 헤더, 페이로드, 시그니처로 이루어져 있다. 헤더에는 토큰 타입과 시그니처의 해싱 알고리즘이 나타나있고, 페이로드에는 토큰에 담아 전달하려는 정보가 담겨있다. 시그니처는 토큰의 검증에 사용한다. 헤더에 사용해야하는 알고리즘이 적혀있지만, 일반적으로는 정해져있다. 예를 들어, 쇼피파이에선 Docs에 HS256을 사용한다고 나와있어서 굳이 헤더를 열어볼 필요가 없다. 그리고 이 알고리즘이 보통 많이 쓰이기도 하고…

네이버는 직접 헤더를 열어서 확인하도록 되어있다. 아직 클로즈베타 기간이라 Docs가 친절하지 않은 걸 수도 있으나.. 까보니

1
{"alg" : "RS256"}

이 있었다. 처음 써보는 알고리즘이긴 했으나 내 코드에 달라지는 부분은 없어서 그냥 처리했는데, pyJWT에서 이 알고리즘을 인식하지 못했다.

설치된 jwt 패키지에서 알고리즘과 관련된 algorithms.py 파일을 보니

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def get_default_algorithms() -> dict[str, Algorithm]:
    """
    Returns the algorithms that are implemented by the library.
    """
    default_algorithms = {
        "none": NoneAlgorithm(),
        "HS256": HMACAlgorithm(HMACAlgorithm.SHA256),
        "HS384": HMACAlgorithm(HMACAlgorithm.SHA384),
        "HS512": HMACAlgorithm(HMACAlgorithm.SHA512),
    }

    if has_crypto:
        default_algorithms.update(
            {
                "RS256": RSAAlgorithm(RSAAlgorithm.SHA256),
                "RS384": RSAAlgorithm(RSAAlgorithm.SHA384),
                "RS512": RSAAlgorithm(RSAAlgorithm.SHA512),
                "ES256": ECAlgorithm(ECAlgorithm.SHA256),
                "ES256K": ECAlgorithm(ECAlgorithm.SHA256),
                "ES384": ECAlgorithm(ECAlgorithm.SHA384),
                "ES521": ECAlgorithm(ECAlgorithm.SHA512),
                "ES512": ECAlgorithm(
                    ECAlgorithm.SHA512
                ),  # Backward compat for #219 fix
                "PS256": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA256),
                "PS384": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA384),
                "PS512": RSAPSSAlgorithm(RSAPSSAlgorithm.SHA512),
                "EdDSA": OKPAlgorithm(),
            }
        )

    return default_algorithms

이렇게 되어있다. pyJWT 패키지만 설치하면, 알고리즘을 HS256, HS384, HS512만 사용할 수있다. has_crypto 변수는 cryptography 라는 패키지를 임포트하는데 성공하면 True 값으로 설정된다. 따라서 cryptography 모듈만 설치해주면 된다. pip3 install cryptography

공개 키의 형식

JWT에는 공개키와 비밀키가 있다. 비밀키는 토큰을 발급하는 쪽(네이버 서버)에서만 알고있는 키고 공개키는 말 그대로 누구나 알 수 있는 키다. 네이버에서 비밀키로 생성한 JWT 토큰을 우리는 공개키를 이용해 디코딩하여 검증해야 한다. 물론 이 공개키로 정당한 토큰을 생성하는건 불가능하다..

다시 원점으로 돌아와 이 공개키는 PEM 형식이어야 한다. 네이버에서는 32jn6jtjjdf98fg332$ 같은 문자열로 제공하는데, 이것 그대로 이용해서 디코딩이 안됐다. 환경변수에서 설정할 때 PEM 형식으로 바꾸었다.

1
2
3
4
5
6
7
# 기존
NAVER_PUBLIC_KEY=32jn6jtjjdf98fg332$

# PEM 형식
NAVER_PUBLIC_KEY=-----BEGIN PUBLIC KEY-----
32jn6jtjjdf98fg332$
-----END PUBLIC KEY-----

이제와서 알았는데, 구글에서 키를 받을 떄 이런 형식으로 받았었는데 왜 이따위로 생겼나 했었다. 이게 PEM 형식이란다. 찾아보니, EC2 접속에 대한 인증으로 생성하고 다운로드 받는 PEM키도 이 형식이라고 한다!

회고

좋은거 배웠다.