인증 방식: Cookie & Session vs JWT

Moen·2022년 9월 30일
0
post-thumbnail
인증(Authentication) vs 인가/권한부여(Authorization)

인증과 인가/권한부여 인증 방식에 대해서 알기 위해서 필요한 사전 지식으로 두 단어의 정확한 의미를 알고 서로의 의미를 혼돈해서는 안됩니다.

인증과 인가를 출입증에 비교해서 설명하는 예를 들어보겠습니다.

인증(Authentication)이란, 방문자가 회사 건물에 출입하려고 할때 확인 받는 과정입니다. 이 과정을 통해서 자신의 신상을 확인할 수 있으며 출입 여부를 확인 받을 수 있습니다.

인가/권한부여(Authorization)이란, 방문자가 회사 건물에 방문했을 때, 허가된 공간에만 접근 가능하며, 개인마다 회사 건물에서 출입할 수 있는 공간의 차이가 존재할 수 있습니다.(vip, 일반 사원, 외부 방문자)

HTTP 요청과 인증의 필요성

HTTP 통신은 현재 WEB 통신 수단으로 가장 많이 사용되는 방식입니다. 그런 HTTP는 비연결성무상태성이라는 특징을 가지고 있습니다. 이런 특징으로 인해서 HTTP 통신은 응답 후 연결을 끊고 과거에 요청한 정보를 기억하지 않습니다. HTTP의 특징으로 인해서 앱을 이용하는 사용자가 여러명이 있을 경우 A 사용자와 B 사용자의 정보 및 콘텐츠가 달라야 하지만 HTTP는 자신의 특징으로 인해서 사용자를 구분할 수 없는 현상이 발생하였습니다. 이러한 문제로 인증을 사용하게 되었습니다.

즉, HTTP는 비연결성 및 무상태의 특징을 가지고 있어서 요청을 보낸 직후 연결을 끊고 정보를 저장하지 않아서 인증이라는 절차를 통해서 확인할 수 있는 수단이 필요합니다.

image

▪︎ 비연결성(Connectionless): 클라이언트와 서버가 한 번 연결을 맺을 후, 클라이언트 요청에 대해 서버가 응답을 마치면 맺었던 열결을 끊어버리는 성질을 말합니다.

▪︎ 무상태(Stateless): 이전 요청과 무관한 각각의 요청을 독립적으로 취급하며 상태를 서버에 저장하지 않는 성질입니다.

Cookie를 이용한 인증

HTTP 쿠키(Cookie)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각으로, 서버를 통해 웹 브라우저에 설치되는 작은 기록 정보입니다. Cookie를 이용한 인증 방식은 가장 쉽게 구현을 할 수 있지만 모든 정보가 public해서 쉽게 정보가 유출 될 수 있는 점을 유의해야 합니다.

인증 방법

  1. 서버는 클라이언트의 로그인 요청에 대한 응답을 작성할 떄, 클라이언트 측에 저장하고 싶은 정보를 응답 해더의 `Set-Cookie에 담습니다. (쿠키는 Key-Value 형식의 문자열입니다.)
  2. 클라이언트에서 인증이 필요한 요청을 보낼 때는 정보를 담은 쿠키를 요청 헤더의 'Cookie'에 담아 보냅니다. (재응답에는 따로 Cookie를 담지 않아도 자동으로 요청시에 Cookie를 담아서 보내는 특징이 있습니다.)
  3. 서버는 클라이언트에서 받은 응답에 포함된 쿠키정보를 바탕으로 요청한 클라이언트를 식별할 수 있습니다.

장점

  • 인증 테스트를 할 경우 및 간단한 앱을 만들때 빠르게 설계할 수 있습니다.

단점

  • 보안에 매우 취약해서 해킹을 당하는 일이 빈번하게 생깁니다.
  • 쿠키에는 용량 제한이 있어 많은 정보를 담을 수 없습니다.(용량을 커질수록 네트워크에 부하 심해져 성능에 문제가 발생할 수 있습니다.)

Session / Cookie를 이용한 인증

Cookie를 이용한 인증 방법은 계정 정보가 public해서 보안에 매우 취약합니다. 보안이 좋지 않다면 인증을 한다고 해도 인증 절차를 신뢰할 수 없습니다. 그래서 보안된 방법으로 Session / Cookie를 이용한 인증 방법이 등장했습니다.

image

인증 절차

  1. 클라이언트에서 로그인을 시도합니다.
  2. 서버에서는 응답 정보를 읽어 사용자를 확인 후, 사용자의 고유한 ID값을 부여하여 세션저장소에 저장한 후, 이와 연결되는 세션ID를 발행합니다.
  3. 사용자는 서버에서 해당 세션ID를 받아 쿠키에 저장을 한 후, 인증이 필요한 요청마다 쿠키를 헤더에 담아 보냅니다.
  4. 서버에서는 쿠키를 받아 세션 저장소에서 대조를 한 후 대응되는 정보를 가져옵니다.
  5. 인증이 완료되고 서버는 사용자에 맞는 데이터를 보내줍니다.

Session / Cookie 인증 방식은 기본적으로 세션 저장소가 필요합니다. 세션 저장소는 로그인 정보를 통해서 사용자의 정보를 저장하고 열쇠가 되는 세션ID값을 만들어 줍니다. 그리고 HTTP 헤더에 세션ID를 담아 사용자에게 보냅니다. 그러면 클라이언트에서는 쿠키로 보관하고 인증을 하는 경우에 요청에 쿠키를 담아 보냅니다. 웹 서버에서는 세션 저장소에서 쿠키(세션ID)를 받고 저장되어 있는 정보와 매칭시켜 인증을 완료합니다.

장점

  • 세션/쿠키 방식은 기본적으로 쿠키를 매개로 인증을 거칩니다. 여기서 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠라고 보시면 됩니다. 따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체(세션 ID)는 유의미한 값을 갖고있지 않습니다(중요 정보는 서버 세션에) 이는 위의 계정정보를 담아 인증을 거치는 것보단 안전해 보입니다.
  • 각 사용자마다 고유한 세션 ID가 발급되기 때문에, 요청이 들어올 때마다 회원정보를 확인할 필요가 없습니다.

단점

  • 쿠키를 탈취당하더라도 안전할 수 있지만. 만일 A 사용자의 HTTP 요청을 B 사용자(해커)가 가로챘다면 그 안에 들어있는 쿠키도 충분히 훔칠 수 있습니다. 그리고 B 사용자는 그 훔친 쿠키를 이용해 HTTP 요청을 보내면 서버의 세션저장소에서는 A 사용자로 오인해 정보를 잘못 뿌려주게 되는 현상이 발생합니다.(세션 하이재킹 공격이라고 합니다)
  • 세션 저장소를 서버에 구축해야하는 노력이 필요하며 추가적인 저장공간이 필요하여 서버에 부하가 걸릴 수 있습니다.

쿠키를 탈취당하는 것을 방지하기 위해서는 HTTPS를 사용하여 요청을 탈취해도 해석할 수 없게 만드는 방법이 있고, 세션의 유효시간을 넣거나 탈취당하면 세션을 파기시키는 방법이 있습니다.


JWT를 이용한 인증

JWT(JSON Web Token)이란 인증에 필요한 정보들을 암호화를 통해서 토큰을 관리하는 것을 의미합니다. JWT 기반 인증은 Session / Cookie 인증 방식과 같이 앱 인증에서 가장 보편적으로 사용되는 방식입니다. 또한 Session / Cookie 인증 방식과 유사하게 사용되는 Access Token(JWT Token)을 HTTP 헤더에 담아서 서버로 보내는 방식입니다.

JWT 구성 요소

image

  • Header: 토큰의 타입과 해시 암호화 알고리즘으로 구성되어 있습니다.
  • Payload: 토큰에 담을 클레임(claim)정보를 포함하고 있습니다. Payload에 담는 저보의 한 조각을 클레임이라고 지칭하며, key / value의 한 쌍으로 이뤄져있습니다.
  • Verify Signature: Base64 방식으로 인코딩한 Header,payload 그리고 SECRET KEY를 더한 후 서명됩니다.
Header, Payload는 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않습니다. 따라서 JWT 토큰에서 Header, Payload는 누구나 디코딩하여 확인할 수 있습니다. 여기서 누구나 디코딩할 수 있다는 말은 Payload에는 유저의 중요한 정보(비밀번호)가 들어가면 쉽게 노출될 수 있다는 말이 됩니다. 하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없습니다. 

인코딩(Encoding): 컴퓨터에서 인코딩은 동영상이나 문자 인코딩 뿐 아니라 사람이 인지할 수 있는 형태의 데이터를 약속된 규칙에 의해 컴퓨터가 사용하는 0과 1로 변환하는 과정을 통틀어 말합니다. (코드화 = 암호화)

디코딩(Decoding): 디코딩은 인코딩의 반대로서 사람이 이해 할 수 있도록 바꿔주는 것을 의미합니다. 즉, 바이트 형식을 문자열로 변환한다는 의미입니다.

A 사용자가 토큰을 조작하여 B 사용자의 데이터를 훔쳐보고 싶다고 가정하겠습니다. 그래서 payload에 있던 A의 ID를 B의 ID로 바꿔서 다시 인코딩한 후 토큰을 서버로 보냈습니다. 그러면 서버는 처음에 암호화된 Verify Signature를 검사하게 됩니다. 여기서 Payload는 B사용자의 정보가 들어가 있으나 Verify Signature는 A의 Payload를 기반으로 암호화되었기 때문에 유효하지 않는 토큰으로 간주하게 됩니다. 여기서 A사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 걸 확인할 수 있습니다.

인증 절차

image

  1. 사용자가 id와 password를 입력하여 로그인을 시도합니다.
  2. 서버는 요청을 확인하고 secret key를 통해 Access token을 발급합니다.(JWT의 유효 기간을 설정합니다.)
  3. JWT 토큰을 클라이언트에 전달 합니다.
  4. 클라이언트에서 API 을 요청할때 클라이언트가 Authorization header에 Access token을 담아서 보냅니다.
  5. 서버는 JWT Signature를 체크하고 Payload로부터 사용자 정보를 확인해 데이터를 반환합니다.
  6. 검증이 완료된다면, Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져옵니다.

장점

  • 세션 저장소를 서버에 구축할 필요가 없어서 서버를 구축할 노력이 필요하지 않아서 간편합니다.
  • 세션 저장소를 서버에 구축하지 않아도 되어서 정보를 담는 메모리를 차지 하지 않아 Statelless 서버를 구축할 수 있습니다.(서버 확장 및 유지보수에 유리합니다 ./ 인증 정보에 대한 별도의 저장 공간이 필요하지 않습니다.)
  • 외부 인증 시스템 Token을 이용한 접근이 가능합니다. OAuth가 가장 대표적인 Token을 이용한 이증 방식으로 대부분의 소셜 로그인이 토큰을 기반으로 외부 인증 시스템을 구축하고 인증을 합니다.
  • 모바일 어플리케이션 환경에서도 잘 동작합니다.

단점

  • Session / Cookie과 다르게 JWT의 Token의 길이가 길어, 인증 요청이 많아질수록 네트워크에 부하가 심해집니다.
  • 한 번 발급된 Token은 유효기간이 만료될 때까지 계속 사용이 가능합니다.
  • Token을 탈취당하면 대처하기 어렵습니다.
  • Payload 자체는 암호화되지 않아서 사용자의 중요 정보를 담을 수 없습니다.
이미 발급된 JWT에 대해서는 돌이킬 수 없습니다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 됩니다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능합니다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 신나게 정보들을 털어갈 수 있습니다. 

해결책: 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급합니다. 그렇게 되면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있습니다. 이는 다음 포스팅에 나올 Oauth2에 더 자세히 다루도록 하겠습니다.

JWT 보안 문제 대체 방안

JWT의 단점을 보안하기 위해서 많은 방법이 하나의 방법만을 사용하기 보다 여러가지 대체 방안을 활용하면 보안 안전에 크게 도움이 됩니다.

짧은 만료 기한 설정

토큰의 만료 시간을 짧게 설정하는 방법을 고려할 수 있습니다. 토큰이 탈취되더라도 빠르게 만료되기 때문에 피해를 최소화할 수 있습니다. 하지만 탈취된 동안은 대처할 수 없는 단점을 완벽하게 해결할 수는 없으며 자주 토큰을 발급받아야 하는 단점도 존재합니다.

Refresh Token

Access Token(JWT)를 통한 인증 방식의 문제는 만일 제 3자에게 탈취당할 경우 보안에 취약하다는 점입니다. 위에서 제시한 만료 기한을 짧게 설정하는 방식이 존재하지만 여러번 로그인을 통한 인증받아야 하는 단점이 있었습니다. Refresh Token을 이용하면 만료 기한을 짧게 설정하고 로그인을 여러번 하는 시도하는 문제를 해결할 수 있습니다.

Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인을 완료했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다.

사용 예를 간단히 들어보겠습니다. Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간이라 하겠습니다. 사용자는 API 요청을 신나게 하다가 1시간이 지나게 되면, 가지고 있는 Access Token은 만료됩니다. 그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있습니다. 

image

인증 절차

  1. 사용자가 ID , PW를 통해 로그인합니다.
  2. 서버에서는 회원 DB에서 값을 비교합니다(보통 PW는 일반적으로 암호화해서 들어갑니다)
  3. 로그인이 완료되면 Access Token, Refresh Token을 발급합니다.
  4. Access Token, Refresh Token을 클라이언트에 전달 합니다.
  5. 사용자는 Refresh Token은 안전한 저장소에 저장 후, Access Token을 헤더에 실어 요청을 보냅니다.
  6. Access Token을 검증합니다.
  7. 검증이 완료되면 이에 맞는 데이터를 클라이언트에 보냅니다.
  8. 시간이 지나 Access Token이 만료됩니다.(여기서 만료라는 개념은 그냥 유효기간을 지났다는 의미입니다.)
  9. 사용자는 이전과 동일하게 Access Token을 헤더에 실어 요청을 보냅니다.
  10. 서버는 Access Token이 만료됨을 확입합니다.
  11. 확인하고 권한없음을 신호로 클라인언트에 응답합니다.
  12. 사용자는 Refresh Token과 Access Token을 함께 서버로 보냅니다.
  13. 서버는 받은 Access Token이 조작되지 않았는지 확인한후, Token이 동일하고 유효기간도 지나지 않았다면 새로운 Access Token을 발급해줍니다.
  14. 서버는 새로운 Access Token을 헤더에 실어 다시 API 요청을 진행합니다.

장점

기존에서 사용하던 JWT(Access Token만을 사용한 인증)보다 더욱 안전한 인증 절차입니다.

단점

  • 구현이 복잡하며, 검증 절차가 로직이 길어집니다. (frontEnd와 Server 둘다)
  • Access Token이 만료될 때마다 새롭게 발급하는 과정에서 생기는 HTTP 요청 횟수가 많습니다. 이는 서버의 자원 낭비로 귀결됩니다.

참고 자료

[REST API]REST를 사용할 때 주의해야할 점

REST - 논문(요약) 훑어보기

REST의 representation이란 무엇인가

그런 rest api로 괜찮은가

profile
게시글에 잘못된 부분이 있으면 댓글로 알려주시면 빠르게 수정 및 수용도 하겠습니다. 🥲

0개의 댓글