글을 쓰게 된 계기

HTTP 요청에서 토큰 인증 방식으로 JWT으로 많이 사용한다. JWT는 토큰의 유효성을 검증하기 위해 서명을 사용하는데, 서명을 생성할 때 사용하는 알고리즘에는 여러가지가 있다. 그 중에서 어떤 것을 사용해야할지 고민하게 되었다.

본 글에서는 JWT의 알고리즘에 대해 알아보고, 어떤 것을 어떠한 상황에서 사용하면 좋을지 정리해보자 한다.

알고리즘에 앞서 JWT의 구조에 대해 간단히 알아보자.




JWT 구조

JWT 공식 홈페이지에 들어가보면, 아래와 같이 인코딩/디코딩을 할 수 있는 툴이 있다.

Screenshot 2024-01-06 at 01 26 43

JWT 구조는 크게 헤더, 페이로드, 서명으로 구분할 수 있다.

  • 헤더: 일반적으로 두 부분으로 구성되는데, 토큰의 유형과 서명된 알고리즘으로 구성된다.
  • 페이로드: 토큰에 전달하고자 하는 클레임이 포함된다. 클레임은 사용자의 정보나 다른 데이터를 포함할 수 있따.
  • 서명: 서명 부분은 헤더와 페이로드를 합친 값을 암호화 알고리즘과 시크릿 값을 사용하여 서명한다. 이 서명을 통해 메시지의 무결성과 메시지가 변경되지 않았음을 검증할 수 있다.

JWT은 위 세 부분을 각각 Base 64로 인코딩하고, 점(.)을 사용하여 하나의 문자열로 만든다.

Screenshot 2024-01-06 at 01 38 25

인코딩 결과를 보면, 헤더와 페이로드는 Base 64로 인코딩되어 있고, 서명은 Base 64로 인코딩되지 않은 것을 확인할 수 있다.

헤더와 페이로드를 인코딩하는 이유는 토큰을 전송할 때 URL-safe 문자열로 전송하기 위해서이다. 서명은 Base 64로 인코딩하지 않는 이유는 서명을 검증할 때 Base 64로 인코딩된 헤더와 페이로드를 사용하기 때문이다.

잠깐 생각해보면, 페이로드에 어떠한 데이터를 넣어서 JWT를 만드는데, 페이로드는 Base 64로만 인코딩되어 있기 때문에 디코딩을 하면 쉽게 데이터를 볼 수 있다. 따라서 페이로드에는 민감한 데이터를 넣지 않는 것이 좋을 것이다.




JWT 알고리즘

이제 서명을 할 때 사용되는 알고리즘에 대해 알아보자.

Screenshot 2024-01-06 at 01 26 38

JWT 공식 홈페이지에서는 사용할 수 있는 알고리즘을 보니 뭐가 많다. 학부 시절에 들었던 보안 수업에서 알고리즘에 대해 배웠던 것 같은데, 그 때는 이런 알고리즘을 실제로 사용할 일이 없을 것 같아서 시험 기간에만 공부하고 넘어갔는데, 다시 만났다. 안녕?

알고리즘 이름을 보면 SXXX(ex: S256, S384, S512) 같은 패턴의 문자열이 붙어 있는 것을 볼 수 있다. 이것이 어떤 것을 의미하는 건지 알아보자


SHA 256, 384, 512

S는 SHA를 의미한다. S256 이라는 단어는 SHA-256을 의미하고, SHA-256은 256비트 길이의 해시를 생성하는 해시 함수이다. 이름을 보면 알수있듯 2^256개의 경우의 수를 만들 수 있고, 블록체인에서 가장 많이 채택되어 사용된다고 한다.

SHA-256 해시를 무차별 대입(brute-force) 방식으로 해독하려는 시도는 이론적으로 가능하지만, 실제로는 엄청난 시간과 계산 자원이 필요하다. 이 때문에 SHA-256은 안전한 해시 함수로 간주되며, 블록체인 같은 분야에서 광범위하게 사용된다.

SHA-384, SHA-512는 SHA-256과 동일한 방식으로 동작하지만, 해시의 길이가 384비트, 512비트로 더 길다. 따라서 더 많은 경우의 수를 만들 수 있고, 더 안전하다고 볼 수 있다.

해시 함수를 사용한다는 것은 여러 가지 조건을 만족해야 한다.

  • 단방향: 복호화 할 수 없어야 한다
  • 동치: 동일한 데이터는 동일한 해시를 생성해야 한다
  • 연산 속도: 해시 함수는 빠르게 동작해야 한다
  • 쇄도 효과: 입력값에 아주 작은 변화만 있어도 해쉬값이 완전히 달라져야 한다
  • 충돌 저항성: 서로 다른 입력값이 같은 해시값을 생성하는 것을 방지해야 한다

해시 알고리즘에 대해서 깊게 파지는 않았지만, 간단하게 어떤식으로 동작하는지 알아보았다.

이제 SHA 해시 함수를 사용하는 알고리즘을 알아보자.


HS256, HS384, HS512

앞선 내용에서 S는 SHA를 의미한다고 했다. 그러면 H가 의미하는 것을 뭘까?

H가 의미하는 것은 HMAC 알고리즘이다. HMAC 알고리즘은 대칭키 알고리즘이다. 즉, 암호화와 복호화에 같은 키를 사용하는 알고리즘이다.

HMAC 동작원리에 대해서는 본 글에서 다루지 않겠다. HMAC의 자세한 동작은 여기를 참고하자.

HS는 HMAC-SHA를 의미한다. HMAC-SHA는 해시 함수를 사용하여 일정 크기의 비트 길이를 만들고 시크릿 값을 사용하여 암호화하고, 복호화할 때도 같은 시크릿 값을 사용한다.

이 때 사용되는 시크릿 값은 암호화/복호화에 모두 사용되기 때문에 시크릿 값은 서버에서 관리하는 게 좋다. 시크릿 값이 유출되면, 토큰이 위조될 수 있다.


RS256, RS384, RS512

그러면 S 앞에 붙은 R이 의미하는 것을 뭘까?

R이 믜미하는 것은 RSA를 의미한다. RSA는 비대칭 알고리즘이다. 즉, 암호화와 복호화에 다른 키를 사용하는 알고리즘이다.

비대칭키 알고리즘은 공개키와 개인키로 암호화하기 때문에 HS와 달리 시크릿 값이 필요하지 않다.

  • 공개키로 암호화한 것은 개인키로 복호화 가능
  • 개인키로 암호화한 것은 공개키로 복호화 가능

RSA 알고리즘도 본 글에서 다루지 않겠다. 자세한 내용은 여기를 참고하자.

RS는 RSA-SHA를 의미한다. RSA-SHA는 해시 함수를 사용하여 일정 크기의 비트 길이를 만들고 공개키를 사용하여 암호화하고, 개인키로 복호화한다.

개인키로 복호화해야 하기 떄문에, 개인키는 서버에서 관리하는 게 좋다. 개인키가 유출되면, 토큰이 위조될 수 있다.




나의 생각

그러면 어떤 알고리즘을 사용해야 할까?

알고리즘을 선택할 때 고려해야 할 사항은 다음과 같다.

  • 보안성: 발급자가 아닌 다른 사용자는 토큰을 위조할 수 없어야 한다
  • 암호화/복호화 시간: 암호화/복호화에 시간이 오래걸려서는 안된다
  • 보안키 관리: 보안키는 유출되면 안된다
  • 토큰 크기: 토큰의 크기는 작아야 한다

고려해야 할 사항들의 특징을 분석하면 시간/보안/토큰 크기에 중점을 맞출 수 있다.

보안성을 생각해봤을 때 HMAC, RSA 알고리즘 모두 안전하다고 볼 수 있다. 또한 일반적으로 HMAC(대칭 알고리즘)이 RSA(비대칭 알고리즘)가 비교적 더 많은 계산이 필요하기 때문에 보다 빠르다. HMAC은 서버에서 관리하는 시크릿 키가 있어야 하고, RSA도 개인키를 서버에서 관리해야 한다. 따라서 보안키 관리 측면에서는 둘 다 보안키를 잘 관리해야 한다. 토큰 크기는 알고리즘에 따라 다르지만, 크게 차이가 나지 않는다고 한다.

4가지 조건을 고려했을 때, 시간적인 면에서 HMAC이 RSA보다 빠르기 때문에 속도를 조금 더 개선하고 싶다면 HMAC 을 사용하는 것이 더 적합하다고 볼 수 있다.

그러면 해시함수로 몇 비트를 사용해야 할까?

비트 수가 많아지면 많아질수록 보안성은 높아지지만, 토큰의 크기가 커지고, 암호화/복호화 시간이 오래걸린다. 즉, 보안과 시간이 trade-off 관계이다.

나는 시간을 위해서 비트수 를 줄여서(256비트) 사용하는 것이 좋다고 생각한다. 주기적으로 JWT의 시크릿 or 개인키/공개키를 변경할 수 있고, 토큰의 유효기간을 짧게 설정하면 보안성을 높일 수 있기 때문이다. 암호화 프로세스는 서비스 운영에 매우 중요하지만, 암호화 프로세스가 느려서 서비스가 느려지는 것은 사용자에게 좋지 않은 경험이라고 생각한다. 암호화/복호화 시간 차이가 심하지 않다면 해시 함수로 512 비트를 사용하고, 크다면 256 비트를 사용하는 것이 좋지 않을까 싶다.




참고

  • https://jwt.io/
  • https://velog.io/@ddangle/JWT-%ED%86%A0%ED%81%B0-%EC%95%94%ED%98%B8%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-HS256%EA%B3%BC-RS256
  • https://medium.com/jongho-developer/jwt-algorithm-hs256-rs256-1ab9f833c486
  • https://en.wikipedia.org/wiki/HMAC
  • https://www.youtube.com/watch?v=P2CPd9ynFLg