Home JWT with Security
Post
Cancel

JWT with Security

JWT를 사용할 때 보안에 대해 고려해야할 부분을 알아봅시다.

What is JWT?

JWT(Json Web Token)은 인증/인가를 위해 세션 방식이 아닌 토큰 형태로 구현하는 기술입니다.
RFC7519에 JWT에 대한 정의가 있습니다.

JWT는 Header, Payload, signature로 3가지로 나누어져 있습니다. JWT구조

Header : 토큰 타입과 서명 알고리즘 정의
Payload : 사용자 정보, 토큰 메타 데이터(Claim)
Signature : 무결성 검증

JWT는 Signature을 기반으로 명시된 알고리즘에 의해 생성됩니다.
Header, Payload 영역은 Base64로 인코딩되어있어 어떤 암호화 알고리즘을 사용하는지, Claim이 어떻게 구성되어 있는지를 알 수 있습니다.

JWT 예시

아래의 예시는 간단한 JWT 토큰의 생성 및 검증 하는 함수입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def create_token(username):
    payload = {
        "user": username,
        "exp": datetime.utcnow() + timedelta(hours=1)
    }
    token = jwt.encode(payload,"secret", algorithm="HS256")
    return token

def verify_token(token):
    try:
        payload = jwt.decode(token, "secret", algorithms=["HS256"])
        return payload
    except jwt.InvalidTokenError:
        return None

JWT와 보안

JWT 자체는 보안 상 안전한 구조이지만 설계 및 관리를 잘못한다면 인증이 무력화 될 수 있습니다. RFC8725를 2.1 섹션부터 JWT에 대한 위협이 명시되어있습니다.

몇가지를 나열 해보면 아래와 같습니다.

  • 약한 서명 및 불충분한 서명 검증
  • 약한 대칭키
  • 암호화 및 서명의 잘못된 구성
  • 타원 곡선 암호화의 안전하지 않은 사용
  • JSON 인코딩의 다양성
  • 대체 공격
  • 교차 JWT 혼동
  • 서버에 대한 간접 공격

대표적으로 3개의 케이스만 작성해 보겠습니다.

약한 서명 및 불충분한 서명 검증

JWT는 Header 영역에 암호화 한 알고리즘이 명시되어 있다고 했습니다.
검증 함수에 만약 암호화 한 알고리즘이 아닌 다른 알고리즘 혹은 None을 허용한다면 어떻게 될까요?
공격자는 RS256 to HS256, None 알고리즘 방식으로 JWT 토큰을 만들어 인증을 하게 되고 검증 함수를 통과하여 인증을 우회할 수 있습니다.

약한 대칭키

HS256 알고리즘은 메시지에 서명 및 검증을 위해 시크릿키를 사용하는데 시크릿키가 엔트로피가 부족하다면 공격자는 무작위 대입 공격, 사전 대입 공격을 이용하여 시크릿키를 얻어 인증을 우회할 수 있습니다.

잘못된 관리

여담으로 쿠팡 정보 유출 경우는 위처럼 잘못된 구성을 통해 취약점을 이용해 인증을 우회한 구조는 아니며 서명키 관리의 문제입니다.

기본적으로 서명키 != 검증키 이지만 어떤 알고리즘을 사용에 따라서 같아질 수도 있습니다.

용어의미
서명키(Signing Key)JWT에 서명을 생성하는 데 사용되는 키
검증키(Verification Key)JWT 서명을 검증하는 데 사용되는 키
시크릿키(Secret Key)대칭키 방식에서 사용하는 단일 공유 키

HS256 같이 대칭키 구조일 경우 서명키 == 검증키 == 시크릿키이며, RS256 같이 공개키 구조일 경우 서명키(개인키) != 검증키(공개키)입니다.

다만 서명키를 알고 있을 경우 대칭키 방식이던 공개키 방식이던 토큰을 만들어 낼 수 있는 구조이긴 합니다.

JWT는 위에서 확인한 내용과 같이 누구나 클라이언트에서 토큰을 만들 수 있는 구조이며, 서버는 검증키를 이용하여 이게 정당한 토큰인지만 확인합니다.

즉 서명키를 알고 있다면, 정당한 접근 권한을 가진 토큰을 만들어 낼 수 있다는 뜻입니다.

추가적으로 누군가의 토큰을 만들려고 한다면 누군가에 대한 식별 정보가 필요합니다.
식별 정보는 ID가 될수도 있고, 고객번호가 될수도 있고, UUID 같은 랜덤 정보가 될 수도 있습니다.

JWT 토큰을 이용하여 3300만명의 정보가 유출이 되었다는 것은 3300만개의 식별 정보를 얻을 수 있었다는 뜻이며, 식별 정보가 시퀀스하게 추측이 가능하다는 얘기입니다.

예시로 UUID 기반으로 식별 정보를 랜덤화 했다면, 인증 정보를 생성할 수 있는 것은 맞지만 3300만개나 되는 정보를 얻기는 쉽지는 않았을 것입니다.

UUID 기반
A -> 550e8400-e29b-41d4-a716-446655440000(UUID)
B -> 3d813cbb-47fb-32ba-91df-831e1593ac29(UUID)

숫자 고객번호 기반
A -> 10001
B -> 10002

이메일 기반
A -> glasses@test.com
B -> noise@test.com

Conclusion

  • JWT를 보안상 문제 없는 인증 방식이다. (단 안전한 설계 및 관리 必)
  • Key는 관리자도 보지 못하게 해야한다.

보안에서는 안전한 암호화 알고리즘을 사용하고, 키 값이 랜덤하게 N글자 이상 만 체크하는 것이 아닌 키 관리 방법 , 키 만료 주기, 키 로테이션 방법 및 주기, 내부 통제에 대한 대안등 고려해야하는 것이 무수히 많아졌다.

요즘 안타까운 것은 보안사고가 나면 지적과 과징금에 대한 압박만 있으며 현실적인 대응 방안 자체가 보이지가 않는다. 당연한 말이지만, 보안 관리 부실한거 아니냐, 패치는 왜 안했고, 자산 식별은 왜 안되는지 지적만 하고 책임을 부여한다.

물론 책임을 지는 것은 당연한 일이지만 사고난 보안담당자들이 몰라서, 일부로, 일하기 싫어서, 업무를 안해서 사고가 났을까? 보안뉴스를 보면서 공감되는 부분이 참 많다.

개인적으로는 이제 뚫린 서버와 뚫릴 서버만 남았다고 생각한다. 이상적이지만 모든 사람이 보안을 신경쓰는 문화가 정착되지 않고, 사고 발생에 대한 원인을 철저하게 분석해서 시스템적으로 발생하지 못하도록 재설계를 하지 않는다면 지금과 같은 보안 사고는 계속해서 발생할 수 밖에 없다고 생각한다.