HTTP란 Hyper Text Transfer Protocol의 줄임말로 www상에서 정보를 주고 받는 통신 방법이다. 주로 HTML문서를 주고 받는데 사용되며 클라이언트와 서버가 서로 request와 response를 하며 통신한다.
HTTP는 Stateless한 특성을 갖는데 서버가 클라이언트의 정보를 계속 유지하고 있지 않는다는 의미이다. 예를 들어 클라이언트와 첫 번째 통신에서 데이터를 주고 받았다고 해도 두 번째 통신에서 클라이언트의 이전 데이터를 유지하지 않는다.
만약 쇼핑몰에서 상품을 장바구니에 담고 구해하는 상황을 가정해보자. 사용자의 정보를 유지하지 않는다면 우리는 계속해서 로그인을 요구해야하는 상당이 귀찮은 상황이 발생한다.
사용자를 이렇게 귀찮게 만들 수는 없으니 Stateless한 프로토콜을 가지고 Stateful한 서비스를 구현하기 위해 사용되는 것이 바로 쿠키와 세션이다.
HTTP웹 사이트에 해당 페이지의 HTML을 요청하면, 응답을 받는 순간 연결이 끊어지게 된다.(계속해서 사용자의 정보를 가지고 있는게 아니다.)
예를들어 쇼핑몰에서 마음에 드는 상품을 골라 클릭하고 해당 페이지를 나오게 되면 컴퓨터는 사용자가 선택한 상품이 어떤건지 몰라 상품 목록을 다시 보여주게 될 것이다.
이런 사용자의 불편함을 방지하기 위해 만들어진 것이 쿠키이다. 로그인 정보같이 유저가 서버어 다시 요청하기에는 비효율적인 정보를 로컬에 저장해둠으로써 생산성을 높이는 것이 목적이다.
쿠키는 서버를 대신해 이러한 정보들을 웹 브라우저에 저장하고 사용자가 요청을 할 때 그 정보를 함께 보내서 서버가 사용자를 식별할 수 있게 해준다.
쿠키가 있기 때문에 여러 페이지를 이동할 때마다 로그인을 하지 않고 사용자 정보를 유지할 수 있는 것이다. 쿠키가 없다면 다음 페이지로 정보를따라 파라미터로 넘겨줘야 한다.
방문했던 웹 사이트에 대한 정보 및 개인정보가 기록되지 때문에 사생활을 침해할 소지가 있으며, 이를 해소하기 위해서 웹 브라우저 자체에 쿠키 거부 기능이 있다. 이러한 쿠키에 대한 거부가 웹 브라우저에 설정되어 있으면 쿠키의 본래목적인 웹 브라우저와의 여결을 지속시키는 기능을 수행할 수 없는 경우가 발생한다.
서버가 가지고 있는 것이 아니라 사용자에게 저장되기 때문에 임의로 고치거나 지울 수 있고, 가로채기도 쉬워 보안이 취약하다. 따라서 쿠키에는 민감하거나 중요한 정보를 담는것은 위험하다.
그래서 이러한 단점들을 보완해주기 위해 Session과 같이 사용한다.
서버에 임시로 저장한 파일을 세션이라고 한다. 주로 중요한 데이터를 저장할 때 사용되고 브라우저를 종료할 때까지 유지 된다.(서버와 클라이언트의 연결이 활성화된 상태.)
쿠키만으로 인증을 사용한다는 것은 서버의 자원은 사용하지 않는다는 것이며 즉, 클라이언트가 인증정보를 책임지게 된다. 클라이언트에 모든 인증 정보가 담여 있다면 HTTP reqest가 해커의 탈취당했을 경우 사용자의 정보는 바로 털리게 되는 것이다.
하지만 이렇게 복잡한 과정을 거치더라고 완벽한 보안은 없다. 이미 인증된 사용자의 HTTP 요청을 해커가 가로챘다면 그 안의 Cookie도 탈취가 가능하다. 그래서 해커가 인증된 사용자의 Cookie를 실어 서버에 요청을 보내면 서버는 인증된 사용자인지 해커인지 구별할 방법이 없다.
토큰은 일종의 암호화된 접근 권한이다. 예를 들어 웹 사이트에 로그인을 할 때, 사용자가 ID와 PW를 입력하면 서버는 이를 확인한 후 사용자에게 유효한 사용자라는 토큰을 발행해준다. 그럼 사용자는 이 토큰을 가지고 웹 사이트에서 제공하는 여러 기능들을 이용할 수 있는 것이다.
좀 더 자세히 말하자면 Token은 인증에 필요한 정보들을 암호화 시킨 토큰을 의미한다. 위의 Session/Cookie 방식과 유사하게 사용자는 Access Token을 HTTP 헤더에 실어 서버에 전송하게 된다.
토큰은 임의로 생성된 비밀번호 같이 동작한다. 토큰은 제한된 수명을 가지고 있어서 토큰이 한 번 만료되면 새로 생성되어야 한다.(Refresh Token)
JWT는 세션/쿠키와 함께 가장 대표적인 인증 수단이다. JWT(JSON Web Token)의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 뜻한다.
세션/쿠키 방법과 유사하게 사용자는 Access Token(JWT토큰)을 HTTP헤더에 실어 서버로 보낸다.
📌 Token 만들기
토큰을 만들기 위해서는 크게 3가지 Header, Payload, Verify Signature가 필요하다.Header : 위 3가지 정보를 암호화할 방식(alg), 타입(type) 등
Payload : 서버에 보낼 데이터. 일반적으로 유저의 고유 ID값, 유효기간
Verify Signature : Base64방식으로 인코딩한 Header, Payload 그리고 SECRET KEY를 더한 암호화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를 기반으로 암호화 되었기 때문에 유효하지 않는 토큰으로 간주하게 된다. 그래서 사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 것이다.
Session/Cookie 방식과 가장 큰 차이점은 Session/Cookie는 Session 저장소에 유저의 정보를 넣는 반면 JWT는 토큰안에 유저의 정보들을 넣는다는 점이다. 클라이언트 입장에서는 HTTP 헤더에 세션 ID나 토큰을 실어서 보내준다는 점에서 동일하나 서버측에서는 인증을 위해 암호화를 하냐 별도의 저장소를 이용하냐의 차이가 발생하게 된다.
간편하다. Session/Cookie는 별도의 저장소 관리가 필요하다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이는 Stateless한 서버를 만드는 입장에서는 큰 강점이다. 여기서 Stateless는 상태(Session/Cookie 정보)를 저장하지 않는 것을 의미한다. 이는 서버를 확장하거나 유지/보수하는데 유리하다.
확장성이 뛰어나다. Token기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어 FaceBook로그인 Google로그인 등은 모두 토큰 기반으로 인증을 한다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 추가할 수 있다.
이미 발급된 JWT에 대해서는 돌이킬 수 없다. Session/Cookie의 경우 만일 Cookie가 악의적으로 이용된다면, 해당하는 Session을 지워버리면 된다. 하지만 JWT는 한번 발급되면 유효기간이 완료될 때까지 계속 사용이 가능하다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 정보를 털어갈 수 있다.
Payload의 정보가 제한적이다. Payload는 따로 암호화 되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다.(Session/Cookie방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관된다.) 따라서 중요한 정보들은 Payload에 넣을 수 없다.
JWT의 길이가 길다. Session/Cookie 방식에 비해 JWT의 길이는 길다. 따라서 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생한다.
*Refresh Token
Access Token(JWT)를 통한 인증 방식의 문제는 제 3자에게 탈취당할 경우 보안에 취약하다는 점이다. 유효기간이 짧은 Token의 경우 그만큼 사용자는 로그인을 자주해서 새롭게 Token을 발급받아야 하므로 불편하다. 그러나 유효기간을 늘리자면, 토큰을 탈취당했을 때 보안에 더 취약해진다. "그러면 유효기간을 짧게 하면서 더 좋은 방법은 없을까?" 라는 고민에 의해 탄생하게 된 것이 Refresh Token이다.
Refresh Token은 Access Token과 똑같은 형태의 JWT이다. 처음에 로그인을 완료 했을때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 된다.
Access Token은 탈취 당하면 정보가 유출되는건 동일하다. 다만 유효기간이 짧기에 조금더 안전하다는 뜻이다. Refresh Token의 유효기간이 만료 됐다면, 사용자는 새로 로그인 해야한다. Refresh Token도 탈취될 가능성이 있기때문에 적절한 유효기간 설정이 필요하다.(보통 2주)
📌 Refresh Token의 과정
1. 사용자가 ID, PW를 통해 로그인한다.
2. 서버에서는 회원 DB에서 값을 비교한다.(보통 PW는 일반적으로 암호화해서 들어간다.)
3. 사용자 인증이 되면 서버에서 Access Token, Refresh Token을 발급, 보통 회원 DB에 Refresh Token을 저장해준다.
4. 서버는 사용자에게 Access Token, Refresh Token 을 보낸다.
5. 사용자는 Refresh Tokendms 은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보낸다.
6. 서버는 Access Token을 검증 후
7. 이에 맞는 데이터를 사용자에게 보내준다.
8. 시간이 흘러 Access Tokendl 만료
9. 사용자는 만료된 Access Token을 헤더에 실어 요청을 보낸다.
10. 서버는 Access Token이 만료된을 확인
11. 만료된 토큰임을 알리고 권한없음을 신호로 보낸다.
12.Access Token이 만료될때 마다 9~11 과정을 거칠 필요는 없다. Access Token의 Payload를 통해 유효기간을 알수 있다. (프론트엔드 단에서 API 요청 전에 토큰이 만료 됐다면 바로 재발급 요청 가능.)
13. 사용자는 Refresh Token과 Access Token을 함께 서버로 보낸다.
14. 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교한다.
15. 서버는 Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 사용자에게 보내준다.
16. 새로운 Access Token을 헤더에 실어 API 요청을 한다.📌 Refresh Token의 장점
- 기존의 Access Token만 있을 때 보다 안전하다.
📌 Refresh Token의 문제점
- 구현이 복잡하다. 검증 프로세스가 길기 때문에 자연스럽게 구현하기 힘들어진다.
- Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많아지게 되고, 서버의 자원 낭비로 이어질 수 있다.
Reference
[web] 쿠키
먹는 쿠키말고 IT 쿠키
서버 인증 방식