본문 바로가기
기타 미분류

[사용자 인증] 인증과 인가의 차이점, 세션과 토큰의 차이점

by domo7304 2021. 9. 6.

 인증은 영어로 Authentication이며, 직관적으로 로그인을 떠올리면 된다. 특정 서비스에 일정 권한이 주어진 사용자임을 증명 받는 것이 인증이며, 말을 달리하여 식별 가능한 정보로 서비스에 등록된 사용자의 신원을 입증하는 과정인증이라고 한다.

 인가는 영어로 Authorization이며, ‘인증을 통해 확인을 받은 사용자가 이후 서비스의 여러 기능들을 사용할 때 내가 로그인이 되어있음을 알아보고 활동을 허가해주는 것, 인증된 사용자에 대한 자원 접근 권한 확인을 의미한다. 인가가 필요한 활동의 예시로는 SNS에서 친구 목록을 보거나, 새로운 글을 작성하거나, 글에 좋아요/댓글을 달거나 하는 등 현재 내가 어떠한 권한을 가진 상태에서만 서버에 요청 가능한 활동들이 인가가 필요한 활동에 해당한다.

 하지만 서버가 해 주어야 하는 일은 단순히 인증과 인가만 있는 것이 아니다, 한 번 인증과 인가가 완료된 사용자에 대해 사용자 편의를 위해 일정 시간, 혹은 기간 동안 해당 권한이 유지되도록 해야 할 것이다. 현재 사용자가 어떠한 페이지에서 글을 쓰기 위해 로그인을 한 상황이라고 가정하자. 사용자가 글을 수정하고자 한다. 그런데 로그인을 유지하기 위한 어떠한 조치도 되어있지 않은 상태라면, 사용자가 글을 작성한 후 수정하고자 할 때 또 다시 로그인을 거쳐 인증/인가를 받아야할 것이다. 이렇게 서비스를 이용하기 위해 매번 인증/인가를 받는 방식은 사용자 경험에서 그리 좋지 못할 것이다. 때문에 로그인을 유지하여 사용자가 더 원활하게 서비스를 이용할 수 있도록 해야한다.

 사용자의 로그인을 유지하는 방법에는 어떠한 것들이 있을까? 크게 세션토큰방식에 대해 설명하고자 한다. 첫번째로 세션에 대해 알아보자. 로그인에서 세션사용자의 로그인과 로그아웃 사이의 활동 기간 이라는 의미를 가지며, 즉 인증과 인가를 마친 사용자는 서버에서 지정한 활동 기간동안 다시 인증/인가를 받지 않아도 됨을 의미한다. 사용자가 로그인 요청을 하고 올바르게 인증이 되었을 경우 서버는 사용자를 구분하기 위한 기한이 짧은 임시키를 발급한 후 브라우저의 로컬 스토리지, 쿠키 등에 저장한다. 이 때 이러한 임시키를 세션을 식별하기 위한 세션 id’라고 한다. 이 세션id를 브라우저 쿠키에 저장했다고 하자. 세션id는 데이터베이스에도 저장되며, 사용자가 어떠한 요청을 보낼 때마다 http 요청에 담긴 세션id 가 서버의 세션id와 일치하는지 확인하는 과정을 거친다.

 이러한 세션 인증/인가 방식은 세션을 서버에서 관리하기 때문에 중간에 세션id 등을 탈취당하더라도 해당 세션을 만료시켜 더 이상 유효하지 않게 만들 수 있는 등 사용자의 인증/인가를 다루는 데에 있어 보안상 유리한 점을 갖는다. 하지만 단점도 존재한다. 서비스가 성장하여 서버를 증설하여 여러 대의 서버를 두고 서비스를 운영하게 되었다고 하자. 이 때, 사용자의 요청에 대해 세션을 발급해준 적절한 서버로 요청을 전달해주어야 하는데, 사용자의 세션 정보를 갖지 않는 다른 서버로 요청을 보내어 사용자 인증/인가에 실패하여 에러가 발생할 수 있다. 이와 같이 서버의 규모가 증가할 경우 적절한 서버로 사용자 요청을 분산시켜줘야 하는 어려움이 존재한다. (이를 해결하기 위해 Redis, MemCached 등의 시스템을 사용한다고 하는데, 나중에 찾아보도록 하자)

 이러한 부담을 줄이기 위해 나온 것이 토큰 방식이며, 토큰 방식 중 JWT를 이용하는 방식에 대해 설명하고자 한다. jwt를 이용하여 인증/인가를 구현할 경우, 사용자가 정보를 입력한 후 서버에 로그인 요청을 보내면 서버는 해당 요청에서 받은 값을 이용하여 토큰을 발급한 후 브라우저의 쿠키 등에 저장하며, 서버나 데이터베이스에서는 이 토큰을 기억하지 않는다. 때문에 세션이 갖는 어려움이나 부담을 줄일 수 있다.

 그렇다면 토큰방식은 어떻게 서버와 데이터베이스에도 저장하지 않고 인증/인가를 할 수 있을까? 이를 알아보기 위해 jwt의 구조를 먼저 살펴보기로 하자. jwtxxxxxx.yyyyyy.zzzzzz 와 같이 (해당 x, y, z에는 다른 영문 대문자와 소문자가 들어가게 된다.) 마침표를 기준으로 세 부분으로 나뉘는데, 맨 앞부터 각 부분을 header, payload, verify signature 라고 하며 headerpayloadbase64, verify signatureheader에서 명시된 알고리즘에 의해 인코딩 된다. 이제 각 부분의 의미를 살펴보자. header를 디코딩하면 type, alg 두 부분으로 나뉜다. typeJWT로 고정값을 가지며, algverify signature 값을 만드는데 사용할 알고리즘이 지정된다. payload에는 사용자 정보 혹은 토큰의 만료기한 등 의미를 갖는 값이 저장된다. 마지막으로 verify signaturejwt의 핵심인데, 앞의 header, payload와 서버에 감추어 놓은 비밀 값 이 세 값을 이용하여 header의 알고리즘에 따라 암호화한 값이 바로 verify signature가 된다. 때문에 서버는 요청에 토큰이 실려오게 되면 header, payload와 서버의 비밀키를 암호화한 값이 요청으로 들어온 토큰과 같은 값을 갖는다면, 사용자를 로그인 된 사용자로서 인증/인가를 해주는 것이다.

 하지만 JWT에도 단점이 존재한다. 서버는 발급한 토큰에 대해 어떠한 것도 저장하고 있지 않기 때문에 사용자에 대한 상태를 저장하며 제어할 수 있는 세션과 달리 이미 발급된 토큰에 대해 어떠한 조치를 취할 수가 없다. 때문에 jwt가 유출되었을 경우 세션과 같이 강제로 비활성화할 수 있는 방법이 없으며, header, payload는 디코딩이 어렵지 않은base64로 인코딩되기 때문에 payload에 민감한 개인정보와 같은 것이 담겨 있었다면 그대로 정보가 유출되는 것이다. 때문에 토큰을 이용할 때에는 되도록이면 민감한 정보를 담지 않아야 한다.

 그러나 토큰 방식에도 이미 발급된 토큰을 다루는 문제에 대한 여러 대안이 있고, 그 중 한 가지를 설명하고자 한다. 바로 access토큰과 refresh토큰 두 종류의 토큰을 발급하여 인증/인가를 진행하는 것이다. 서버는 사용자가 로그인을 할 때 access토큰, refresh토큰 두 가지의 토큰을 발급해준 후 access토큰의 만료 기한을 가깝게 잡고, refresh 토큰은 데이터베이스에 저장한다. 이후 access 토큰이 유효하지 않은 상태에서 사용자가 서버에 요청을 보내게 되면 서버는 데이터베이스에 저장된 refresh 토큰과 비교하여 그 결과에 따라 다시 access 토큰을 발급해주는 것이다. 하지만 이 또한 access 토큰과 refresh 토큰의 탈취로부터 완벽히 자유롭지는 못하며, 결과적으로 서버에서 발급한 토큰을 데이터베이스에 저장한다는 것은 세션 방식과 유사하다고도 볼 수 있으므로 완전한 해결책이라고 볼 수는 없다.

 마지막으로 세션방식과 토큰방식의 차이를 살펴보자. 세션은 사용자의 상태를 기억하고 제어할 수 있기 때문에 stateful하다고 하며, 토큰은 그 반대인 stateless하다고 한다. 세션과 같이 stateful하여 모든 사용자들의 상태를 기억해야하는 것은 구현하기도 어렵고 고려사항도 많지만, 성공하기만 한다면 기억하는 대상의 상태를 언제든 제어할 수 있다는 장점을 가진다. 예를 들어 한 기기에서만 로그인이 가능한 서비스를 만드는 경우 pc에서 로그인을 한 사용자가 모바일에서 또 로그인을 하려 한다면 pc에서는 로그아웃 되도록 기존 세션을 만료할 수 있다. 이렇듯 사용자의 상태를 다루는 데에 있어 세션이 갖는 이점이 있기 때문에 사용자 인증/인가 문제를 전부 토큰방식으로 처리할 수는 없지만, 앞서 jwt의 장점에서 이야기하였듯 사용자의 모든 상태를 데이터베이스가 저장해야하는 세션 방식과 다르게 토큰 방식은 정해진 알고리즘으로 header, payload, 비밀키를 암호화하여 브라우저에 넘겨준 후에는 서버나 데이터베이스에 어떠한 것도 기억할 필요 없이 사용자 요청이 있을 때마다 토큰 값을 비교하여 인증/인가를 해주면 되므로 상대적으로 서버/데이터베이스에서 처리해야할 작업이 가볍다는 이점을 가진다.

 

 

찾다보니 이런 말도 나오는데 나중에 찾아볼 필요가 있겠다

clientserver간의 통신규약인 HTTP, server가 지향하는 REST API는 무상태성 즉 stateless를 지향하는데, 세션을 이용하여 사용자가 로그인하면 세션id를 만들고, 이걸 DB에 저장하고, client 브라우저에서도 저장하고, 또 다른 요청이 있으면 또 서버를 거쳐 DB의 세션정보와 일치하는지 확인하고

, client, server, database모두 사용자의 정보, 상태 상태성을 가지게 된다. statelessstateful이라는 두 패러다임이 충돌을 한다!

앞으로 인증과 인가에 대해 더 공부해본다면 좋을 키워드들

oAuth, 인증서버, HTTPONLY, Sliding Session / Refresh Token, SSL/TLS 1.3.

 

댓글