2022. 8. 9. 10:28ㆍ카테고리 없음
이 글은 JWT의 내부 구조, 동작 원리를 떠나서 처음 접했을 때 내가 들었던 궁금증에 관한 글이다.
꼭 JWT 가 아니라 토큰 방식에 대한 궁금증이라고 할 수 있다.
JWT 를 학습하면서 여러 의문이 들었다.
이게 왜 안전하지?
토큰을 서버가 아닌 클라이언트에서 관리한다면 탈취하면 끝 아닌가?
만들어진 토큰을 사이트에서 디코딩하면 내용이 다 나오는가 아닌가?
토큰을 클라이언트가 가지고 있으면 이게 사용자인지 어떻게 알지?
이런 의문에 자문자답하는 시간을 가져보겠다.
우선 인증과 인가는 글을 이해하는 데 도움을 줄 수 있는 내용이다. 잘 몰랐던 내용이라 함께 정리한다.
인증과 인가
인증은 사용자의 신원을 확인하는 프로세스이다.
예를 들어, 내가 신분증을 제출할 때나 전화로 이름, 거주지, 주민등록번호 앞자리를 물어보는 것들이 해당한다.
애플리케이션에서 인증은 아이디, 패스워드를 통한 로그인이 대표적이다.
인가는 접근 권한을 확인하는 프로세스이다.
예를 들어, 콘서트 티켓이나 사원증, 도어락 키를 통해 입장하는 것들이 해당한다.
이것들은 내가 누구인지가 중요하지 않다. 다만 권한을 가지고 있음을 증명하는 것이 있다는 것만이 중요할 뿐이다.
이 글을 작성하게 된 계기가 된 토큰을 주고받는 것이 애플리케이션에서 인가에 해당한다.
토큰을 서버가 아닌 클라이언트에서 관리한다면 탈취하면 끝 아닌가?
맞다. 토큰은 서버에서 인증을 통과했기에 발급해준 증명서와 같다.
즉, 본인의 집 열쇠를 탈취당했다고 생각해보자. 그 열쇠를 가진 사람은 집에 들어올 수 있을 것이다.
다만, 토큰을 탈취하는 것은 어렵다(당장 생각해봐도 HTTPS 같은 보안 계층이 있는 상태에서 내 클라이언트에 접근해서 토큰을 가져가는 것은 쉽지 않다).
또한 애플리케이션에서 여러 보안 장치를 해둘 수 있다.
새로운 환경에서 구글, 네이버 로그인을 할 때 아이디 패스워드를 적었어도 별도의 인증 요구를 받아본 적이 있을 것이다.
이처럼 IP 주소에 따라 다시 인증받아야 하도록 제한하거나 서버에서 토큰의 유효 기간을 정해주는 방법으로 보안 장치를 해둘 수 있다.
그러므로 탈취하기가 쉽지 않고 어느 정도 대비를 할 수 있다.
토큰을 사이트에서 디코딩하면 내용이 다 나오는 거 아닌가?
JWT 토큰은 헤더, 클레임(페이로드), 시그니처 세 부분으로 나누어진다.
여기서 헤더와 클레임은 암호화한 것이 아니라 JSON을 base64로 인코딩한 것이기 때문에 디코딩하면 값이 나온다.
그래서 다른 누군가가 위변조할 수 없도록 검증하는 부분이 시그니처다.
시그니처는 다음 과정을 통해 생성한다. 자세한 건 링크 를 참조하자.
1. JSON에서 공백을 제거한다.
String header = '{"alg":"HS256"}'
String claims = '{"sub":"Joe"}'
2. UTF-8 바이트와 Base64URL 인코딩을 각각 가져온다.
String encodedHeader = base64URLEncode( header.getBytes("UTF-8") )
String encodedClaims = base64URLEncode( claims.getBytes("UTF-8") )
3. 인코딩된 헤더와 클레임을 사이에 마침표 문자로 연결한다.
String concatenated = encodedHeader + '.' + encodedClaims
4. 연결한 인코딩 값을 암호화 알고리즘(Ex.HMAC-SHA-256)과 함께 지정한 비밀(개인)키를 사용해 시그니처를 만든다.
Key key = getMySecretKey()
byte[] signature = hmacSha256( concatenated, key )
위와 같이 헤더 + 클레임의 인코딩 값에 암호화 알고리즘과 비밀키를 사용해 시그니처를 만들기 때문에 안전한 것으로 간주할 수 있다. (신뢰성)
만약 전달하는 내용이 위변조(헤더나 클레임이 변경) 되었다면 내용을 기반으로 만들어진 시그니처와 값이 다를 것이다. 그러므로 JWT가 조작되거나 변경되지 않았음을 보장할 수 있다. (무결성)
토큰을 클라이언트가 가지고 있으면 어떻게 사용자인지 알지?
지금까지의 내용에 이 질문에 대한 답이 들어있는 듯하다.
이 토큰을 발급받았다는 것 자체가 사용자 인증을 받았다는 것을 의미한다.
어떤 권한을 가졌는지, 유효기간은 어떻게 되는지 정도만 넣기 때문에 어떤 사용자인지까지는 알 수 없다. (물론 클레임에 사용자 정보를 넣으면 확인할 수야 있겠지만 보안을 고려하면 해서는 안 된다.)
토큰은 어떻게 검증할 수 있을까?
암호화 알고리즘에 따라 공개키를 사용하기도 대칭키를 사용하기도 한다.
이 때문에 많이 혼동이 왔는데 우선 난 HMAC-SHA-256 을 기준으로 말하겠다.
HMAC-SHA-256 는 대칭키 방식을 사용한다. 그래서 서버가 토큰을 받았을 때 가지고 있는 비밀키를 이용해 유효성을 검증할 수 있다.
JWT를 학습하면서 생겼던 의문들을 정리해보았다. 토큰 방식에 대해 감이 안 잡히는 분들이 도움이 되었으면 한다.
틀린 내용이 있는 경우, 댓글 남겨주시면 감사하겠습니다.
참고
https://blog.outsider.ne.kr/1160
https://meetup.toast.com/posts/239
https://auth0.com/intro-to-iam/authentication-vs-authorization/