앞선 2주차에, http의 특징들을 공부하며 http가 가지는 특징인 무상태성(stateless)에 대해 알아봤다. stateless라는 특징이 가지는 장점(효율성 및 확장성)도 있지만, 단점(서버가 이전 요청을 기억하지 못한다)도 존재했다.
이러한 단점을 해결할 수 있는 기술들에는 쿠키, 세션, jwt(토큰) 등이 있다.
쿠키는 클라이언트(브라우저)에서 상태를 저장하고, 이 정보를 요청때마다 자동으로 서버에 전달하는 방식이다.
특정 요청에 대해 서버가 상태를 알아야 하거나, 클라이언트의 요청을 식별해야 하는 경우 쿠키를 생성하여 클라이언트로 전달한다.
쿠키를 받으면 브라우저는 이후 같은 도메인으로 요청을 보낼 때마다 쿠키를 자동으로 포함시켜서 보낸다.
-> 서버는 클라이언트가 보낸 쿠키를 통해 사용자의 상태를 알 수 있다.
쿠키는 설정된 도메인에서만 사용이 가능하다 -> 보안 강화
이 부분에 대해 프로젝트를 진행하면서 공부했던 내용이 있다.
세션은 서버에서 상태를 저장하고, 클라이언트는 session id만 전달하여 서버가 상태를 찾을 수 있게 하는 방식이다.
클라이언트가 서버에 요청을 보내면, 서버는 session id를 생성해서 클라이언트에게 보낸다 (일반적으로 쿠키에 저장된다).
클라이언트는 이후 요청마다 session id를 포함하여 서버에 보내고, 서버는 이를 통해 세션을 조회한다.
session id만 쿠키에 담아서 전송되기 때문에 다른 방식에 비해 더 안전하다.
서버에서 상태 관리상태 정보를 서버에서 관리하므로 데이터 저장에 더 유연하다.
서버에 상태를 저장하는 만큼 서버 자원 소모가 크다.
서버가 여러 대일 경우, 세션 동기화 처리가 등 별도의 처리가 필요하다.
토큰 방식은 클라이언트가 토큰을 보유하고 있으며, 서버는 이 토큰을 통해 사용자의 상태를 인증하고 유효성 검사를 하는 방식이다.
클라이언트의 요청에 대해 서버는 토큰을 발급하여 클라이언트에 전달한다.
클라이언트는 이후 요청 시 header 또는 쿠키에 토큰을 포함시켜 요청을 보낸다.
서버는 이 토큰의 정합성을 검증하고, 토큰에 포함된 정보를 기반으로 사용자 상태 등을 확인한다.
서버는 토큰을 저장하지 않고 클라이언트에서 상태를 관리하기 때문에 확장성이 좋다.
분산 환경에서 상태를 서버 간 공유할 필요 없이 클라이언트 측에서 관리할 수 있다.
JWT와 같은 토큰은 종종 크기가 클 수 있으며, 매번 요청 시 토큰을 포함시켜야 하기 때문에 성능에 영향을 줄 수 있음.
토큰(jwt)의 특징에는 서명(Signature)과 암호화(Encryption)가 있는데, 비슷해보이지만 다르다.
서명은 데이터의 무결성(integrity)을 보장하기 위해 사용된다.
서명된 JWT는 서버만 알고 있는 비밀 키(secret key)를 사용하여 생성되며, 이 서명으로 서버에서는 데이터가 변경되지 않았음을 검증할 수 있다.
하지만 서명은 데이터를 암호화하지 않아서, 서명된 JWT의 내용을 누구든지 볼 수 있다.
즉, 서명은 데이터의 진위 여부를 확인하는 역할을 하며, 내용을 암호화하지는 않는다.
암호화는 데이터를 숨기는 역할을 한다.
JWT에 암호화를 적용하려면, 별도의 JWE (JSON Web Encryption)
표준을 사용해야 하며, JWE는 JWT의 내용을 암호화하는 방법이다. JWE를 사용하면 토큰의 header, paylead 뿐만 아니라 signature도 암호화할 수 있다.
따라서, JWE를 사용하지 않은 토큰은 Base64 디코딩만으로 확인 가능하다.
토큰의 서명과 암호화에 대해 공부하고 난 후, '대부분의 웹 서비스에서는 당연히 JWE를 사용하겠구나' 라고 생각했지만, 찾아보니 다음과 같은 이유로 인해 대부분 JWE를 사용하지 않는다고 한다.
일반적인 웹 서비스에서는 대부분 JWE 같은 암호화 기술을 사용하지 않고, JWS 서명, HTTPS, 다양한 전략들을 통해 충분한 보안을 확보한다.
대부분의 웹 서비스는 HTTPS를 사용하고, 요청 자체가 HTTPS로 암호화되어 전송되기 때문에 네트워크 구간에서 토큰이 탈취될 가능성은 매우 낮다.
암호화된 토큰은 크기도 커지고, 복호화 처리도 추가로 필요해서 성능에 영향을 줄 수 있다. 서버 간 통신에서도 JWS로 충분한 경우가 많고, JWE까지 쓰는 건 성능과 개발 복잡도 측면에서 부담되는 경우가 많다.
실제 서비스에서는 토큰의 payload에 비밀번호, 주민번호 같은 민감한 정보는 절대 넣지 않는다. 대부분의 경우 role, username 정도
를 넣기 때문에, 탈취되더라도 큰 피해가 생기지 않도록 설계한다.
ATK의 TTL을 짧게 설정함으로써 피해를 최소화하는 방식을 사용한다.
RTK를 HttpOnly+Secure를 적용한 쿠키로 저장한다.
로그아웃 처리 시 서버에서 Redis 등에 RTK blacklist를 추가하여 해당 토큰을 이용한 요청을 막는다.