5주차 과제를 진행하면서, 클론 코딩 프로젝트에 JWT
를 적용해보았다. 그 과정속에서 학습한 JWT
를 비롯한 인증 방식에 대해 기록하고자 한다.
인증 방식이란 무엇일까? 간단하게 설명하자면, 놀이공원에 입장하고 놀이기구를 이용하기 위해서는 다양한 방법이 있다. 자유이용권
, Big 5
, 단일 발권
등등.. 여기서 각각의 방식에 대입해서 인증 방식을 간단하게 정리해보자.
앞서 말한 놀이기구를 이용하기 위한 여러 가지 방법 중에서 자유이용권은 어떤 속성을 갖고 있는지 생각해보자. 단순하게, 자유이용권은 놀이공원이 개장한 순간부터 폐장하는 순간까지 손목에 잘 차고만 있으면 모든 놀이기구를 이용할 수 있다. 하지만 만약 끊어져서 잃어버린다면? 그리고 끊어진 자유이용권을 다른 누군가가 주운 뒤, 본인의 손목에 이어서 붙여버린다면? 물론 그런 일이 있어서는 안되겠지만, 자유이용권을 주운 사람 또한 모든 놀이기구를 이용할 수 있게 될 것이다. 이와 같이, 자유이용권과 유사한 인증 방식이 Session & Cookie
방식이다. 단순히 Session ID
를 가지고 있기만 하면 모든 권한을 갖게 되는 것이다. 그만큼 보안에 취약하기도 하다. 반대로 말하면 Session ID
가 노출되는 순간 주인이 가진 모든 권한을 타인도 가질 수 있다는 뜻이다.
따라서, Session & Cookie
인증 방식의 흐름을 간략하게 설명하면 다음과 같다.
세션은 서버에서 가지고 있는 정보이며 쿠키는 사용자에게 발급된 세션을 열기 위한 열쇠(SESSION ID)를 의미한다. 만약 쿠키만으로 인증을 사용한다면 그건 서버의 자원을 사용하지 않는다는 것이며, 이는 클라이언트가 인증 정보를 책임진다는 말과 같다. 그렇게 되면 HTTP 요청을 탈취당할 경우 정보가 다 털리게 된다. 따라서 보안과는 상관이 없는 단순한 기능은 장바구니나 자동로그인과 같은 경우에는 유용하게 쓰일 수 있다.
Session & Cookie
방식은 기본적으로 쿠키를 매개로 인증을 거치는데, 여기서 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠라고 보면 된다. 따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체(세션 ID)는 유의미한 값을 갖고있지 않습니다(중요 정보는 서버 세션에) 이는 계정정보를 요청에 담아 인증을 거치는 것보단 안전할 것이다(당연하게도).
사용자 별로 고유의 ID값을 발급받게 된다. 그렇게 되면 서버에서는 쿠키 값을 받았을 때 일일이 회원정보를 확인할 필요 없이 바로 어떤 회원인지를 확인할 수 있기 때문에 서버의 자원에 접근하기 용이할 것이다.
만약 쿠키를 탈취당한다면, 실제 쿠키의 사용자가 누구인지와는 관계없이(놀이공원의 자유이용권과 마찬가지로) 사용할 수 있다. 즉, 타인의 정보로 요청을 보낼 수 있다는 뜻이다(다른 사람 명의로 결제를 한다던지 뭐.. 그런..).
서버에서 세션 저장소를 사용해야하므로 서버에 추가적인 저장공간을 필요로 하게 되며, 자연스럽게 비용과 부하도 늘어나게 된다.
Open Authorization, Open Authentication
OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는 접근 위임을 위한 개방형 표준이다.
다음은 OAuth
이다. 앞서 말한 놀이기구를 이용하기 위한 방법 중에 Big 5
에 해당한다고 볼 수 있다. Big 5
에 대해서 간단하게 설명하자면, 놀이기구 5개를 탈 수 있는 티켓이라고 보면 된다. 하지만 여기서는 조금 더 확장해서 허용된, 즉 정해진 놀이기구만 탈 수 있는 티켓이라고 보면 될 것 같다. 그런데 이때 티켓을 제 3자에게 부여받아서 사용하는 것이다. 네이버 로그인, 카카오 로그인등이 있다. 하지만 세션과는 다른 점이 있다. 예를 들어서, 처음에 Big 5
티켓을 발권받고 놀이기구를 이용하다보니 티켓을 모두 소진했다고 하자. 다시 놀이기구를 이용하기 위해서는 다시 티켓을 발급받아야 한다(....그럴거면 자유이용권을...). Session ID
는 갖고만 있으면 계속해서 유효하지만 그와 달리 OAuth
는 권한이 유효한 순간이 정해져있다. 이 기간은 짧은 편이므로 만료되기 전에 갱신해줘야 한다.
OAuth
는 쉽게 말해서 다른 서비스의 회원 정보를 안전하게 사용하기 위한 방법이라고 생각하면 된다. 여기에서 안전하게의 주체는, 회원 정보를 가지고 있는 주체, 우리의 고객이다. 즉, 우리의 고객이 안전하게 다른 서비스의 정보를 우리 서비스에 건네주기 위한 방법이다. 고객이 자신의 소셜 서비스의 아이디/비밀번호를 우리 서비스에 알려주지 않아도, 소셜 서비스에 있는 고객의 정보를 우리 서비스에서 안전하게 사용하기 위한 방법이다.
그렇다면 OAuth
의 인증 과정은 다음과 같이 간략하게 정리할 수 있다.
콜트월드(가명) 발권업무 담당 직원(이하 직원), 콜트(놀러온....)가 있다고 하자.
Big 5
티켓을 달라고 한다.이는 결국 소셜 서비스에서 발급한 Access Token
을 얻기 위한 절차이다. 소셜 서비스가 가진 유저의 정보를 요청하기위해선 해당 Access Token
이 필요하기 때문이다.
이러한 토큰을 우리 서비스에서 전달받기 위해서 Redirect
를 이용한다. 우리에게는 QueryString이 있기 때문이다. 그러니까, 소셜 서비스에서 로그인에 성공하면 우리 서비스에서 정의한 특정 URI
로 redirect
하면 된다는 것이다. QueryString
에 토큰의 값을 묶어서!
그렇게 하면 우리 서비스에서 소셜 서비스에서 발급한 토큰을 받을 수 있게 된다.
그래서 우리 서비스에서 소셜 서비스로 로그인 요청을 보낼 때 redirect_uri
를 지정해서 보내게 된다. 하지만 이 요청이 탈취되어 redirect_uri
가 수정되어 악의적으로 사용되는 것(XSS, 피싱 사이트)을 방지하기 위해, OAuth
를 사용하려면 우선 해당 서비스에 등록절차를 밟아야 한다. 즉, 소셜 로그인 기능을 사용하려고 한다면 사전에 해당 소셜 서비스에 등록을 하고 승인을 받아야 한다.
그리고 이러한 등록 과정에서 여러 가지 정보를 기입하는데, 이 때 redirect_uri
도 사전에 미리 지정해주게 된다. 이렇게 함으로써 지정된 redirect_uri
가 아닌 다른 값으로 redirect
한다면 소셜 서비스는 이 요청을 받아들이지 않을 것이다.
이제 해당 Access Token
을 이용해서 소셜 서비스에서 제공하는 API
의 URI
로 요청하면 소셜 서비스에서 정보를 얻어올 수 있다.
Access Token
을 가지고 있더라도 허용된 권한만 사용할 수 있게 하기위해 토큰과 함께 보내는 Permission List
라는 것도 있다.
다음은 JWT
이다. JWT
는 마치 손목에 도장을 찍어주는 것과 비슷하다고 볼 수 있다. 매번 놀이기구를 이용 할 때마다 손목에 찍힌 도장을 보여주는 것이다.
로그인에 성공하면 서버는 인코딩한 문자열을 토큰인것처럼 보내게 되는데, 클라이언트는 요청을 보낼 때마다 이를 헤더에 실어서 같이 보내어 권한을 검증하게 된다. 이를 JWT
라고 하며, 인증에 필요한 정보들을 암호화시킨 토큰을 뜻한다. SSL
통신(HTTPS
)을 통해 패킷 자체를 못 보게 할 수 있다(확률을 낮추는 것.. 뚫으려고 작정하면...). 그래서 JWT
에는 SSL
을 반드시 적용해주는 것이 좋다.
JWT에 들어가보면 암호화된 토큰을 볼 수 있다.
토큰을 만들기 위해서는 크게 3가지, Header
, Payload
, Verify Signature
가 필요하다.
Header
, Payload
그리고 SECRET KEY
를 더한 후 서명된다.최종적인 결과 : Encoded Header
+ "."
+ Encoded Payload
+ "."
+ Verify Signature
Header
, Payload
는 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않는다. 따라서 JWT
토큰에서 Header
, Payload
는 누구나 디코딩하여 확인할 수 있다. 그 말은 곧, Payload
에 중요한 정보를 담으면 쉽게 노출될 수 있기 때문에 주의해야 한다는 뜻이다.
하지만 Verify Signature
는 SECRET KEY
를 알지 못하면 복호화할 수 없다.
A 사용자가 토큰을 조작하여 B 사용자의 데이터를 훔쳐보고 싶다고 가정하자. 그래서 payload에 있던 A의 ID를 B의 ID로 바꿔서 다시 인코딩한 후 토큰을 서버로 보냈다고 하자. 그러면 서버는 처음에 암호화된 Verify Signature를 검사하게 된다. 여기서 Payload는 B사용자의 정보가 들어가 있으나 Verify Signature는 A의 Payload를 기반으로 암호화되었기 때문에 유효하지 않는 토큰으로 간주하게 된다. 여기서 A사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 걸 확인할 수 있다.
JWT
의 인증 과정을 간략하게 정리하면 다음과 같다.
간편하다. Session & Cookie
는 별도의 저장소 관리가 필요하다. 하지만 JWT
는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이는 상태를 저장하지 않는(Stateless) 서버를 만드는 입장에서는 큰 강점이다. 서버를 확장하거나 유지 또는 보수하는데 유리하기 때문이다.
확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어 Facebook 로그인
, Google 로그인
등은 모두 토큰을 기반으로 인증을 하는데, 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다.
이미 발급된 JWT
에 대해서는 조작이 불가능하다. Session & Cookie
의 경우 만일 쿠키가 악의적으로 이용된다면 해당하는 세션을 지워버리면 된다. 하지만 JWT
는 한번 발급되면 유효기간이 만료되기 전까지는 계속 사용이 가능하다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 계속해서 정보를 탈취할 수 있다.
- 기존의 Access Token
의 유효기간을 짧게 하고 Refresh Token
이라는 새로운 토큰을 발급함으로써 Access Token
이 탈취당했을 때 발생할 수 있는 피해를 줄일 수 있다.
Payload
정보가 제한적이다. Payload
는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. 따라서 유저의 중요한 정보들은 Payload
에 넣을 수 없다.
JWT
의 길이이다. Session & Cookie
에 비해 JWT
의 길이는 길다. 따라서 인증이 필요한 요청이 많아질수록 서버의 자원낭비가 발생하게 된다.
우와 콜트님 ~ 깔끔 그 자체군요 ? 정말 도움되는 글입니다 !
저도 스프링부트 시작하고 싶네요 ㅎㅎ~