이번 강의에서는
JWT 토큰의 세번째 부분인 서명(Signature)에 대해
이해해 보도록 합시다
이전에 논의한 바와 같이
서명은 JWT 토큰 속 선택적인 부분입니다
JWT 토큰 속 디지털 서명과도 같은 것이고
이는 생성된 토큰을
조직 속 내부 애플리케이션과 공유할 때마다
선택적으로 포함할 수 있습니다
그리고 클라이언트 애플리케이션이
토큰과 방화벽 속 모든 소통을
조작하지 않을 것이라는 신뢰가 있다면
굳이 디지털 서명을
할 필요는 없습니다
모든게 조직 내에 방화벽 속에 있기 때문에
클라이언트 애플리케이션 또는 그 누구도
네트워크 속을 조작하지 않을 것이라
신뢰할 수 있다면
문제가 되지 않습니다
하지만 로그인 과정 중에 생성된
JWT 토큰을 인터넷을 통해
엔드 유저와 고객들이 이용할 수 있는
클라이언트 애플리케이션들과 공유하고 있다면
그리고 해당 토큰이
인터넷 네트워크 속에서 이동한다면
반드시 아무도 권한, 아이디와 같은
헤더와 바디 값들을
바꾸지 않도록 해야 합니다
예시로 유저의 역할을 가진
엔드 유저가 있다고 가정해 봅시다
그는 애플리케이션에 로그인을 하여
JWT 토큰을 수신하였습니다
그가 JWT 토큰이 어떻게 돌아가는지
이해하고 있는 해커라면
토큰 내의 역할 정보를
유저에서 관리자로 간단하게 바꿀 수 있을 것입니다
이렇게 역할을
높일 수 있게 되어버리면
해당 평문을 다시
base64Encoder로 변환할 수 있습니다
인코딩과 디코딩은 간단한 과정이기 때문에
암호화 키나 해싱을 필요로 하지 않습니다
너무 너무 간단한 작업이기 때문에
나쁜 엔드 유저나 나쁜 클라이언트 애플리케이션
혹은 JWT 토큰을 액세스한 해커는
언제든지 이러한 토큰을 조작할 수 있는 것입니다
그렇다면 어떻게 JWT 토큰
네트워크나 클라이언트 애플리케이션
혹은 엔드 유저로부터의 조작을 방지할 수 있을까요?
우선 JWT 토큰 속
서명 부분이 있습니다
백엔드 애플리케이션이 해야할 일은
새로운 JWT 토큰을 생성할 때마다
성공적인 인증 이후에는
토큰에 디지털 서명을 해야 합니다
그렇다면 이를 어떻게 하느냐
SHA-256와 같이
알고리즘 중 하나의 도움을 받습니다
해당 알고리즘은 헤더, 내용
정보를 전달하며 이와 함께
백엔드 애플리케이션에만 알려져 있는 비밀을 사용합니다
보시다시피 알고리즘의 입력은
(base64UrlEncode(header)
그 뒤에 + "." 점
그리고 + base64UrlEncode(payload)
마지막으로는 , secret)
따라서 해당 비밀 값은
JWT 토큰을 발행하는
백엔드 애플리케이션만 알 수 있습니다
이 알고리즘의 출력은
전달한 데이터를 기반으로 한
무작위 해시 문자열입니다
그리고 해당 문자열은 JWT 토큰의
서명 부분으로 보냅니다
이제 헤더, 내용, 서명으로 구성된
세 부분을
클라이언트에게 다시 보내고
클라이언트는 같은 토큰을 보내야 합니다
그리고 만약 최종 사용자
클라이언트 애플리케이션
또는 해커 중 누군가가 토큰의 헤더
내용, 서명 값 등
토큰을 조작하려 한다면
무엇을 조작하려고 하는지와 관계없이
백엔드 애플리케이션은 이를 쉽게 감지하고
해당 세션을 무효화시킬 것입니다
이를 어떻게 할 수 있는지 이 플로우 차트를
이용하여 이해해 보도록 합시다
보시다시피 헤더 부분과
내용 (payload)부분이 있습니다
헤더와 바디/내용 그리고 비밀 값에
백엔드 애플리케이션은
서명 부분을 생성했을 수 있습니다
그리고 해당 서명 부분은 내부적으로 해시 값을 갖고 있는
해시 문자열입니다
그리고 같은 토큰이 클라이언트 애플리케이션에게
다시 보내집니다 예를 들어
클라이언트 애플리케이션이 후속 요청을
다시 보내 오는 것입니다 백엔드 애플리케이션에서는
이제 그 JWT 토큰을 클라이언트 애플리케이션
또는 엔드 유저로부터 받았기에
무엇을 하냐면
동일한 공식, 동일한 알고리즘
헤더의 동일한 Base64 URL 인코더
그리고 점(dot), 다음에 페이로드의 동일한 Base64 URL 인코더를 사용하여
초기에 사용된 것과 같은 비밀값과 함께
새로운 서명 해시를 계산하려 할 것입니다
따라서 출력은 또
새로운 서명 해시가 되는 것입니다
그리고 백엔드 애플리케이션은
새롭게 생성된 해시의
해시 값들을
JWT 토큰 자체에 있는
이미 생성된 해시 값과 비교할 것입니다
예를 들어
누군가가 헤더 혹은 내용를 조작하려 한다면
저는 당연히 새로운 서명을 계산해내려 할 것이고
기존 서명 부분에 저장되어 잇는 것과는
다른 해시 값이 될 것입니다
그리고 누군가 서명을 직접적으로
조작하려는 경우에는
해시 값이 당연히
절대 같지 않을 것입니다
따라서 이는 JWT 토큰을 스토리지 시스템 내에
저장하지 않고도 JWT 토큰이 조작 되었는지
확인할 수 있는 좋은 방법입니다
그리고 이는 클라이언트 애플리케이션과
백엔드 애플리케이션에게
그 누구도 우리의 JWT 토큰을 건들 수 없을 거라는
100프로의 신뢰감을 줍니다
누군가 조작하려 한다면
해당 세션 전체가 무효화 되는 것입니다
우리가 계산하려는 서명이 무엇인지
여러분에게 명확하게 전돨 되었기를 바랍니다
이는 우리가 이전에 bcrypt 해싱 알고리즘을 사용한
패스워드 인코더 부분에서 논의했던
'패션' 컨셉을 사용합니다
매우 비슷한 상황으로
내부적으로는 해시 문자열이
외부 해시 문자열과 매우 다르게 보일 수 있지만
내부적으로는 모두 동일한 값을 가지고 있습니다
JWT 토큰에 대해 조금 더
자세히 이해하고 싶다면
jwt.io라는 사이트를 방문할 수 있습니다
여기에 JWT 토큰을 입력하면
해당 토큰의 헤더와 내용 (payload)의 내용을 표시해줍니다
물론 서명 내용은 해시된 문자열이기 때문에
표시할 수 없습니다
하지만 헤더와 내용 속에
무엇이 있는지 쉽게 이해할 수 있습니다
해당 사이트를 접속하여
빠르게 보여 드리겠습니다
여기가 제가 말한 jwt.io라는 사이트입니다
가지고 있는 JWT 토큰 중 아무거나 붙여넣기 해보면
이렇게 됩니다
여기가 헤더 부분이며
그 다음에 첫 점 이후로는 내용입니다
그리고 이 마지막 부분은 서명 부분입니다
여기 보시면
이 헤더 부분은 이 값으로 디코딩 된 것입니다
비슷하게 이 페이로드 역시
이러한 평문으로 디코딩 된 것입니다
서명 부분을 보시면
해시열이기 때문에 디코딩 될 수 없습니다
따라서 이는 백엔드 애플리케이션에서
서명을 확인하는 데 활용할
공식입니다
토큰을 보다 잘 이해하기 위한
일상 속 예시를 들자면
호텔의 경우를 떠올릴 수 있습니다
호텔에서 체크인하는
상황을 떠올려 보십시오
첫 날에는 체크인을 하기 위해
프론트로 갑니다
본인에 대한 자격증명을 인증하기 위해
여권과 같은 신분증으로 정보를 인증하고
예약 내역을 보여줄 것입니다
프론트에서 해당 정보가 확인이 되면
카드를 발급해줄 것입니다
해당 카드로는 호텔 내에서
다양한 일을 할 수 있습니다
본인의 방으로 들어갈 수 있고
바, 식당, 헬스장 등을 출입할 수 있습니다
같은 카드로 호텔 내에서는 계속 사용할 수 있습니다
그리고 해당 카드를 잃어버리게 된다면
프론트에 다시 찾아가면 가야합니다
이때 프론트에서는
이전에 발급해준 카드를 무효화 시킨 후
신분증과 같이 증빙 자료를 다시 한번 제공할 것을 요청할 것입니다
그리고 해당 정보가 확인이 된다면
새로운 카드를 발급해 줄 것입니다
이는 토큰의 시스템과도 유사합니다
백엔드 애플리케이션에게
자격을 증명하기 위한 증빙 자료와
네트워크에 대한 정보를 계속해서 제공하는 대신
토큰의 도움을 받을 수 있는 것입니다
토큰이 한번 발행되고 나면 해당 토큰의 수명에 따라
백엔드 애플리케이션 속 보호된 자료를 액세스 하기 위해
토큰을 사용할 수 있습니다
이제 JWT 토큰에 대해서 완전히 이해 하셨길 바랍니다
다음 강의부터는 이러한 JWT 토큰을
웹 애플리케이션 내에 구현해보겠습니다
그럼 다음 강의에서 뵙겠습니다
감사합니다