JWT는 JSON Web Token의 약자로 JSON 형식의 웹 토큰을 의미합니다. 이를 통해 사용자 로그인 인증을 진행하거나 당사자 간에 정보를 교환할 수 있습니다. 이번 포스팅에서는 JWT란 무엇이며, 사용 이유, 토큰 발급과 사용 방법에 대해 알아보도록 하겠습니다.
HTTP의 특성
JWT에 대해 알아보기 전에 HTTP의 중요한 특성을 이해하고 있어야 합니다. HTTP는 서버와 클라이언트가 데이터를 주고 받는 대표적인 통신 프로토콜 중 하나입니다. 이러한 HTTP 프로토콜은 각 연결과 상태가 독립적이라는 특성을 갖고 있습니다!
즉, HTTP는 connectionless(연결을 유지 하지 않음)하며, stateless(상태를 유지하지 않음)합니다(쉽게 말해 전에 무슨 일이 있었는지 전혀 모르고, 또 알려고도 하지 않는 특성입니다). 따라서 쿠키 또는 세션 등 이전 정보를 기억할 수 있는 추가적인 조치가 없다면, 사용자가 로그인 한 다음 페이지를 이동하면 바로 다시 로그인을 해야하는 불편한 일이 발생할 수 있습니다.
사용자 로그인을 처리하는 두 가지 방법
로그인 이야기를 계속 이어가 보겠습니다. 웹사이트에 로그인하는 사용자가 페이지를 이동할 때마다 새롭게 로그인해야 한다면 불편할 뿐더러 개인 정보가 유출될 수 있는 위험이 존재합니다. 웹에서 로그인을 처리하는 대표적인 방법으로는 세션 방식과 토큰 방식이 있으며, 다음과 같은 차이점이 존재합니다.
- 세션 방식: 서버의 메모리 또는 데이터베이스 등에 사용자의 정보를 저장하는 방식입니다. 서버는 사용자를 확인한 다음 세션 ID를 발급합니다. 토큰 방식에 비해 보안이 좋으나 서버의 자원이 상대적으로 많이 필요합니다.
- 토큰 방식: 서버에서 발행하는 토큰을 사용자의 브라우저에 저장하는 방식입니다. 세션 방식에 비해 트래픽에 대한 부담이 적으나, 토큰이 유출될 경우 누구나 정보를 확인할 수 있어 보안이 상대적으로 떨어집니다. 또한 토큰에 담기는 데이터 크기가 길어지므로 잦은 요청 시 데이터 트래픽에 영향을 미칠 수 있습니다.
이번 글에서는 JWT 토큰 방식을 사용한 인가와 로그인 정보 전달에 대해 살펴보도록 하겠습니다. 이를 위해 먼저, JWT의 구조를 이해할 필요가 있습니다.
JWT 구조의 이해
JWT 구조를 파악하고 시크릿 키를 통해 토큰을 발행하면 사용자 로그인을 처리할 수 있습니다. 그럼 이제, JWT 구조에 대해 본격적으로 알아보도록 하겠습니다.
JWT를 인코딩 및 디코딩할 수 있는 다음 사이트에 들어가보시면 JWT의 작동 방식을 직관적으로 살펴볼 수 있습니다. 왼쪽이 인코딩된 토큰이며, 오른쪽은 인코딩하기 전 데이터(또는 디코딩 된)입니다. 오른쪽의 데이터가 하나라도 변경되면, 인코딩 된 값도 변합니다.
JWT는 세 가지 파트인 header
, payload
, signature
로 구성됩니다. 아래와 같은 구조를 가지며 .
을 통해 구분됩니다. XXX
는 헤더, YYY
는 페이로드, ZZZ
는 서명 부분에 해당합니다.
XXX.YYY.ZZZ
각 파트에 대해 조금 더 자세히 살펴보도록 하겠습니다.
헤더(header)
헤더에는 JWT를 검증하는 암호화 알고리즘, 토큰 타입 등이 포함됩니다.
{
"alg": "HS256",
"typ": "JWT"
}
페이로드(payload)
토큰에 담아 전송하는 데이터가 포함됩니다. 이러한 각 정보를 클레임(claim
)이라 하며, 이는 키-값으로 구성되어 있습니다. 클레임은 사전 정의된 registered clam, 개발자가 정의한 public claim, 두 주체 간에만 교환되는 기타 private claim으로 구분됩니다.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
헤더와 페이로드는 데이터를 16진수로 인코딩하는 것이며 암호화를 진행하는 것이 아닙니다. 따라서 누구나 이를 디코딩하여 정보를 확인할 수 있습니다. 즉, JWT에 담아 전달하는 값에는 민감한 정보를 담지 않아야 합니다.
서명(signature)
서명을 위해서는 인코딩된 헤더와 페이로드, 시크릿 키, 알고리즘이 필요합니다. 서명을 통해 토큰을 인코딩 된 당시의 환경과 대조할 수 있으며, 진위 여부를 확인할 수 있습니다.
access_token = jwt.encode({
"sub": "1234567890",
"name": "John Doe",
"admin": true
}, SECRET_KEY, algorithm='HS256')
이러한 세 부분을 모두 조합하면 다음과 같은 JWT 토큰이 생성됩니다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.dyt0CoTl4WoVjAHI9Q_CwSKhl6d_9rhM3NrXuJttkao
JWT를 사용한 로그인 처리 방법
이제 JWT 토큰의 특성과 구조에 대해 이해했습니다. 다음과 같은 플로우를 통해 JWT를 활용하여 사용자 로그인을 처리할 수 있습니다.
- 사용자가 아이디와 패스워드를 입력하여 로그인
- 서버는 시크릿 키(secret key)를 통해 접근 토큰(access token) 발급
- 사용자에게 JWT 전달
- 로그인이 필요한 API 호출 시 헤더(header)에 JWT를 담아 전송함
- 서버에서 JWT 서명을 확인하고 시크릿 키로 JWT를 디코드하여 사용자 정보를 획득
- 서버에서 유저를 인식하고 요청 사항에 응답함
참고 자료:
'Auth | Security' 카테고리의 다른 글
XSS 공격이란? (0) | 2022.03.13 |
---|---|
SSO 싱글사인온이란? (0) | 2022.03.08 |
SSH 개념과 사용 방법: 퍼블릭 키와 프라이빗 키 설명 (1) | 2022.03.01 |