서버 인증 방식(세션/쿠키, 토큰)

하태현·2021년 9월 6일
29

인증방식

Session/Cookie 방식

쿠키는 일종의 서버와 클라이언트가 대화하기 위한 수단.

  • 브라우저가 서버와 연결이 되었을 때 브라우저에서 자동적으로 쿠키를 생성하고, response 할 때 쿠키를 담아서 보낸다.
  • 특정 호스트에서 생성된 쿠키는 이후 모든 요청마다 서버로 전송됨
  • 요청 해더의 set-cookie 속성에 정보를 담을 수 있음.
  • 쿠키에 담긴 데이터는 브라우저에서 관리됨.
  • 이름, 값, 만료 날짜, 경로 정보로 구성.

Session

서버와 클라이언트의 연결이 활성화된 상태.

  • 클라이언트가 서버와 통신을 시작하면 서버는 해당 클라이언트에 대해 유일한 값인 세션 id를 부여, 세션 스토리지에 세션 정보를 저장함.

  • 클라이언트는 이 세션id를 쿠키를 통해 기억함.

  • 이후 클라이언트가 어떤 요청을 보낼 때마다 헤더의 cookie에 세션 id를 담아서 전송함.

  • 서버는 클라이언트가 보낸 요청의 쿠키에 담긴 세션 id와 세션 스토리지에 담긴 세션 id를 대조해 인증 상태를 판단함.

    (즉, 세션과 쿠키는 완전히 분리된 개념이 아니며 세션은 쿠키를 기반으로 함)

  • 각 클라이언트마다 유니크한 세션 객체가 주어지고, 이 세션 객체에 데이터를 담아 관리할 수 도 있음.

    (세션 객체가 자물쇠로 잠긴 상자라면 세션 id 가 열쇠인 셈)

  • 세션을 사용하지 않고 쿠키만으로 어떤 데이터를 주고받는다면, 클라이언트는 이미 모든 데이터를 알고 있다는 것.

  1. 사용자 로그인 요청
  2. 서버에서 계정 정보를 읽어 사용자를 확인 하고, 사용자의 고유한 ID를 부여하여 세션 저장소에 저장한 후, 이와 연결된 세션ID를 발급한다.
  3. 사용자는 서버에서 해당 세션ID를 받아 쿠키에 저장 한 후, 인증이 필요한 요청마다 쿠키를 헤더에 실어 보낸다.
  4. 서버는 쿠키를 받아 세션 저장소에서 대조 후 대응되는 정보를 가져온다.
  5. 인증이 완료 되고 서버는 사용자에 맞는 데이터를 보내준다.

쿠키만으로 인증을 사용한다는 것은 서버의 자원은 사용하지 않는다는 것이며, 즉 클라이언트가 인증 정보를 책임 지게 된다. 클라이언트에 모든 인증정보가 담겨 있다면 HTTP request가 해커의 의해 탈취 당했을 경우 사용자 정보는 바로 털리게 되는것이다.(약 2년간 모의해커로 근무한 경험이 있는데, 실제 국내 많은 기업들이 이러한 이유로 개인정보를 노출 시키는 경우가 많다.)
그래서 장바구니나 자동로그인 설정의 경우 유용하게 쓰인다.

결과적으로 Session/Cookie 인증 방식을 사용하는 이유는 서버에 인증의 책임을 전가하는 것이다.

장점

  1. 세션/쿠키 방식은 기본적으로 쿠키를 매개로 인증을 거친다. 여기서 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠라고 보면 된다. 따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체(세션 ID)는 유의미한 값을 갖고있지 않다(중요 정보는 서버 세션에)
  2. 사용자 A는 1번, 사용자 B는 2번 이런식으로 고유의 ID값을 발급받게 된다. 그렇게 되면 서버에서는 쿠키 값을 받았을 때 일일이 회원정보를 확인할 필요 없이 바로 어떤 회원인지를 확인할 수 있어 서버의 자원에 접근하기 용이할 것이다.

문제점

하지만 이렇게 복잡한 과정을 거치더라도 완벽한 보안은 없다.
이미 인증된 사용자의 HTTP 요청을 해커가 가로 챘다면 그 안의 Cookie도 탈취가 가능하다.
그래서 해커가 인증된 사용자의 Cookie를 실어 서버에 요청을 보내면 서버는 인증된 사용자인지, 해커인지 구별할 방법이 없다.

해결법

  1. HTTPS사용, 서버와 클라이언트 간의 주고받는 정보를 암호화하여 요청을 탈취해도 정보를 읽을 수 없다.
  2. 세션에 유효시간을 지정한다. (일정 시간이 지나면 해당 클라이언트와 서버와의 세션을 끊는다.)

토큰 기반 인증 방식(jwt)

Token

인증을 위해 사용되는 암호화된 문자열.

  • 사용자가 인증에 성공하면 서버는 토큰을 생성해서 클라이언트로 보낸다.
  • 토큰도 세션과 마찬가지로 사용자가 보내는 요청에 포함 된다.
  • 세션 인증에서는 서버가 세션ID를 저장하고 클랑이언트가 쿠키에 실어보낸 세션ID와 대조해서 확인 하는 반면,
    토큰을 사용하면 요청을 받은 서버는 토큰이 유효한지를 확인만 함.
  • 세션 인증에 비해 서버 운영의 효율이 더 좋다.

JWT는 세션/쿠키와 함께 가장 대표적인 인증 수단이다. JWT(JSON Web Token)의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 뜻한다.
세션/쿠키 방법과 유사하게 사용자는 Access Token(JWT토큰)을 HTTP헤더에 실어 서버로 보낸다.

토큰을 만들기 위해서는 크게 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를 알지 못하는 이상 토큰을 조작할 수 없다는 것이다.

  1. 사용자가 로그인을 한다.
  2. 서버에서는 계정 정보를 읽어 사용자를 확인 후, 사용자 고유ID값을 부여한 후, 기타 정보와 함께 Payload에 넣는다.
  3. JWT의 유효기간 설정
  4. 암호화할 SECRET KEY를 이요해 Access Token을 발급 한다.
  5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 실어 보낸다.
  6. 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작여부, 유효기간을 확인한다.
  7. 검증이 완료 되면, Payload를 디코딩 하여 사용자의 ID에 맞는 데이터를 가져온다.

세션/쿠키 방식과 가장 큰 차이점은 세션/쿠키는 세션 저장소에 유저의 정보를 넣는 반면, JWT는 토큰안에 유저의 정보들을 넣는다는 점이다. 클라이언트 입장에서는 HTTP 헤더에 세션ID나 토큰을 실어서 보내준다는 점에서는 동일하나, 서버측에서는 인증을 위해 암호화를 하냐 별도의 저장소를 이용하냐의 차이가 발생한다.

장점

  1. 간편하다. 세션/쿠키는 별도의 저장소 관리가 필요하다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이는 Stateless 한 서버를 만드는 입장에서는 큰 강점이다. 여기서 Stateless는 상태(쿠키/세션 정보)를 저장하지 않는 것을 의미 한다. 이는 서버를 확장하거나 유지/보수 하는데 유리하다.
  2. 확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능. 예를들어 Facebook 로그인, Goggle 로그인 등은 모두 토큰을 기반으로 인증한다. 이에 선택적으로 이름이나 이메일 등을 받을 수있는 권한도 추가할 수 있다.

문제점

  1. 이미 발급된 JWT에 대해서는 돌이킬 수 없다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 된다.
    하지만 JWT는 한번 발급되면 유효기간이 완료될 때까지 계속 사용이 가능하다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 정보를 털어갈수 있다.
  2. Payload의 정보가 제한적이다. Payload는 따로 암호화 되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관) 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없다.
  3. JWT의 길이. 세션.쿠키 방식에 비해 JWT의 길이는 길다. 따라서 인증이 필요한 요청이 많아질 수록 서버의 자원 낭비가 발생한다.

해결법

  1. 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급한다. 그렇게 되면 Access Token을 탈취 당해도 상대적으로 피해를 줄일수 있다.

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주)

  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. 만료된 토큰임을 알리고 권한없음을 신호로 보낸다.
    Access Token이 만료될때 마다 9~11 과정을 거칠 필요는 없다. Access Token의 Payload를 통해 유효기간을 알수 있다.
    따라서 프론트엔드 단에서 API 요청 전에 토큰이 만료 됐다면 바로 재발급 요청 가능.

  12. 사용자는 Refresh Token과 Access Token을 함께 서버로 보낸다.

  13. 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Refresh Token과 사용자의 DB에 저장되어 있던 Refresh Token을 비교한다.

  14. 서버는 Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 사용자에게 보내준다.

  15. 새로운 Access Token을 헤더에 실어 API 요청을 한다.

장점

  1. 기존의 Access Token만 있을때 보다 안전하다.

문제점

  1. 구현이 복잡하다. 검증 프로세스가 길기 때문에 자연스럽게 구현하기 힘들어짐(서버/클라이언트 모두)
  2. Access Token이 만료될 때 마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많다. 이는 서버의 자원 낭비로 이어질수 있다.
profile
왜?를 생각하며 개발하기, 다양한 프로젝트를 경험하는 것 또한 중요하지만 내가 사용하는 기술이 어떤 배경과 이유에서 만들어진 건지, 코드를 작성할 때에도 이게 최선의 방법인지를 끊임없이 질문하고 고민하자. 이 과정은 앞으로 개발자로 커리어를 쌓아 나갈 때 중요한 발판이 될 것이다.

4개의 댓글

comment-user-thumbnail
2021년 11월 12일

홀리~ 깨달았네요

답글 달기
comment-user-thumbnail
2022년 6월 15일

정말 정리 잘하신것 같아요 감사합니다!

답글 달기
comment-user-thumbnail
2022년 8월 13일

정말 잘 정리해주셨네요! 좋은글 감사합니다! ㅎㅎ

답글 달기
comment-user-thumbnail
2023년 7월 4일

정말 놀랍고 저에게 도움이 됩니다. Super Mario

답글 달기