Stateful Vs. Stateless

Ajisai·2024년 2월 4일
0

말 그대로 생각하면 상태가 있냐 없냐인데, 그럼 의문점이 두 개가 생긴다.

  1. 상태는 무엇인가
  2. 어디에 있냐 없냐가 기준인가
    스포) 서버임

상태

  • 클라이언트의 정보
  • 이전 요청에 대한 정보
  • 인증 및 인가 정보
  • 이외에도 꽤나 많은 것들이 포함된다.

그게 뭔 말인가요

보통 웹 페이지를 사용하려면 로그인을 해야 한다. 로그인에는 인증(Authentication)과 인가(Authorization)가 포함된다.
인증과 인가도 꽤나 헷갈리는 개념인데, 단순하게 말하면 다음과 같다.

  • 인증: 저 맞습니다
  • 인가: 저 자격 있습니다

그러니까 A가 웹 페이지를 사용하려면, 일단 내가 A임을 증명해야 하고(인증), A는 웹 페이지를 사용할 자격이나 권한이 있음을 증명(인가)해야 하는 것이다.
이런 정보가 상태의 대표적인 예시다.

Stateful

  • 상태가 있냐 없냐의 기준은 서버다.
  • 다시 말해 클라이언트의 정보를 서버에 저장해두냐 아니냐의 차이.
  • stateful하다 = 서버에 클라이언트 정보를 저장한다
  • 즉 한 번 로그인하면 나의 대한 정보를 서버에 저장해둔다.
  • 대표적인 예가 session.
  • 일단 로그인하고 세션이 생성되었다면, 이후의 요청에서는 굳이 또 나를 증명하지 않는다(세션이 만료되지 않는 한).
  • 요약) 한 번 말해두면 당분간 기억한다.

Stateless

  • stateless하다 = 서버에 클라이언트 정보를 저장하지 않는다
  • 대표적인 예가 JWT; Json Web Token.
  • 클라이언트의 인증 정보는 클라이언트에 저장한다.
    그래서 요청에 인증 정보가 포함되어 들어오고, 서버는 여기서 요청자가 누군지 확인한다.
    JWT에 사용자 식별자가 포함되어 온다면, 그 사용자는 인증된 사용자임을 확신한다.
    • 진짜 그 사람이 보낸 건지 확인하지 않는다(인증은 하지 않는다).
    • 단 그 사람이 우리 서비스 사용자인지는 확인한다(인가는 한다)
  • 클라이언트를 무조건 믿는 방식같기도...
  • 요약) 말해도 기억을 안 해서 했던 말 또 해야 된다.

요청 검증

JWT를 사용한다면 다음과 같은 검증을 생각해볼 수 있다.

사용자 A의 정보가 담긴 토큰으로 글쓰기를 요청한다.
글쓰기 요청에는 다음이 포함된다.

  • 토큰
  • 작성자
  • 게시글 제목
  • 게시글 내용

이 경우 다음을 고려해봐야 한다.

  • 토큰에 포함된 사용자 정보와 작성자 정보가 일치하는가?
  • 토큰이 만료되지는 않았는가?

토큰이 만료되지는 않았는가? 는 모든 요청에서 당연히 고려할 부분이다.
토큰에 포함된 사용자 정보와 작성자 정보가 일치하는가?를 생각해보자.
이 부분을 만족하려면 다음과 같은 절차를 거쳐야 한다.

  1. 토큰에서 사용자 정보를 얻는다.
  2. 요청에서 작성자 정보를 얻는다.
  3. 둘 사이의 일치를 확인한다.

이걸 확인하는 건 토큰이 탈취된 상황을 고려하기 위함이다.
그런데 요청 시마다 요청을 검증하는 건 stateful에 가깝다.

stateless에서는 사용자 정보를 유지하지 않는 것이 핵심이다. 즉 서버 쪽에서 사용자 정보를 굳이 확인하는 게 아니라, 애초에 토큰을 잘 보냈어야 하는 것.
어느 유튜버의 비유에 따르면 '신용카드 잃어버리면 쓴 놈 잘못이지 발급한 놈 잘못이 아니다'.
즉 토큰을 발급한 서버에서는 탈취된 상황을 고려하지 않는 것이다.

물론 진짜 고려 안하면 망한다. 고객이 카드를 잃어버렸는데 아무런 조치도 취하지 않는 카드사는 매일 논란에 휩싸일 것이다.
그래서 access token과 refresh token으로 구분해 운용한다.
운용법은 이 블로그의 다른 글을 참고 바란다.

Vs. stateful

  • stateful이라면
    • 내가 인증됐다는 걸 서버가 기억한다(나의 인증 정보를 서버에 저장한다).
    • 따라서 게시글 목록을 요청할 때는 나라고 다시 말하지 않아도 서버가 나인 걸 알고 응답해준다.
  • stateless라면
    • 내가 인증됐다는 걸 서버가 모른다. 그 정보는 나만 안다.
    • 따라서 로그인했으면 게시글 목록을 요청할 때 "나 인증 됐어요"라고 다시 말해야 한다. 그렇지 않으면 서버는 나를 모르는 사람 취급한다.

stateless를 쓰는 이유는

우선 생각나는 것은 서버를 여러 대 두는 상황에서 유리하다는 것이다.

stateful하다면

  1. 내가 서버 A를 통해 인증했다고 하자.
  2. 그런데 내가 사용하려는 서비스는 여러 대의 서버를 통해 분산 처리를 하고 있다.
  3. 나는 A를 통해 인증했기 때문에 서버 A에만 내 인증 정보가 저장된다.
  4. 그런데 게시글을 보려고 요청을 보냈더니 그 요청을 서버 B에서 받았다.
  5. 서버 B에는 나에 대한 인증 정보가 없으니 인증을 요구한다.
  6. 나는 분명 인증을 했는데 또 인증하라고 하니까 서운해

stateless하다면

  1. 서버 A를 통해 인증했다고 하자.
  2. 이 인증 정보는 서버 쪽이 아니라 나에게 저장된다.
  3. 그래서 나는 다른 요청을 보낼 때마다 인증 정보를 포함해서 보낸다.
  4. 그러면 내 인증 정보가 만료되지 않는 한 서버 B가 요청을 받든 C가 받든 나는 인증된 사용자로 인식된다.
  5. 이제 안서운해

그럼 stateless가 신이고 무적 아닌가요

  • 일단 서버는 서버가 받은 요청에 포함된 인증 정보를 '믿는다'.
  • 즉 일단 인증 정보 자체가 유효하다면(내가 발급했고, 아직 만료되지 않았고, ...) 그 인증 정보에 따라 응답한다.
  • 다시 말하면 인증 정보만 갖고 있다면 뭐든 할 수 있다는 건데, 이렇게 되면 인증 정보 탈취에 굉장히 취약해진다.
    • 토큰만 몰래 빼가면 난 인증된 사용자처럼 행동할 수 있는 것이다.
    • 인증 정보의 유효성은 검사하지만 그 인증 정보가 누구의 것인지, 즉 보낸 사람의 것이 맞는지 모른다.
  • 그래서 JWT에서 access token과 refresh token이라는 귀찮은 운용 방법을 취하는 것이다.

CSRF는 어떡하나요

HTTP의 헤더 중 Authorization이라는 게 있다.
이 헤더에 인증 정보를 담으면 CSRF로부터 안전한데, 이유는 다음과 같다.

  • JWT의 경우 토큰을 로그인 시 발급받고 클라이언트 측에 저장해둔다.
  • 그리고 필요하다면 헤더에 포함해(헤더에 담는 경우 자동으로 포함된다) 서버에서 인증하는 방식이다.

아니 그러면 공격자가 사용자에게 니가 받은 토큰을 헤더에 포함해서 이런 요청을 보내라고 스크립트를 짤 수도 있는 거 아닌가요?

그건 사용자 컴퓨터 자체가 털린 거라 JWT고 뭐고 답이 없는 상황입니다(출처)

profile
Java를 하고 싶었지만 JavaScript를 하게 된 사람

0개의 댓글

관련 채용 정보