Stateless Authentication

June 2, 2017

Stateless Authentication

이번 글에서는 Stateless Authentication에 대해 이야기해보려한다.

Stateless Authentication란 무엇일까. 일단 직역하면 무상태 인증이다. 여기서 인증에 대해서는 쉽게 생각하면 서비스 로그인 정도로 생각하면 될 것이다. 그런데 무상태가 무엇인지, 왜 나왔는지 이해하려면 먼저 HTTP의 세션과 REST에 관한 이야기를 안할 수 없다.

Http Session

일반적으로 예전의 경우에는 거의 무조건, 현재도 상당히 많은 곳에서는 웹서비스에 로그인을 하여 사용자를 식별하기 위해 Cookie를 사용한다. 하지만 Cookie는 서버가 아닌 브라우저에 저장되는 데이터이기 때문에 사용자에 관한 민감한 인증 정보를 담고 있을 수는 없다. 따라서 웹어플리케이션서버는 사용자마자 특정한 세션이라는 것을 자신의 메모리 공간에 할당하고 이에 대한 세션ID만을 Cookie에 저장하여 브라우저에 전달한다. 그리고 민감한 정보는 모두 서버의 세션에 저장되는 것이다. 보안적인 이유로 봤을 때 이는 자연스럽다. 사용자 A가 B서비스로부터 할당받은 세션ID를 누군가에게 탈취당하지만 않는다면 누군가 A인척하고 서비스를 이용할 수는 없을것이다. (트래픽암호화 및 탈취당해도 안전하도록 설계하는 것에 대해서는 논외로한다) 그런데 문제가있다. 사용자가 조금일때는 별 문제가 없었지만 A가 낸 입소문이 일파만파 퍼져 사용자가 너무많이 늘어난 것이다. B는 수많은 트래픽 처리를 위해 서버를 나누고 두대에서 골고루 처리하도록 했지만 세션정보를 다루는 것에 대한 고려사항이 생긴다. 어쩌다 서버1에 로그인한 사람은 계속 서버1이 처리하지 않으면 안되는 것이다. 이는 분산취지에 조금 어긋나는 것 같다. 이에 관한 분산 세션관리에 대해서는 많은 대표적인 방법이 존재하고 B는 그 중 적절한 방법을 택해서 처리해왔다. 이는 여간 귀찮은 일이 아닐 수 없다.

RESTful

한편, 일각에서는 REST가 등장했다. REST는 HTTP를 고안했을때 의도했던 부분을 충분히 활용하고 있지 않는다는 문제를 지적하며 떠오르기 시작했다. REST가 말하고자 하는 것은 여러가지가 있지만 우리는 그중 Stateless에만 집중해보기로 한다.

REST가 말하는 Stateless란, 세션과 같이 서버가 항상 사용자 요청을 처리하기 위해 사용자에 관한 상태정보가 필요하면 안된다는 것이 핵심이다. 즉 별도의 세션정보없이 들어오는 요청만을 통해 사용자에게 정보를 제공해야한다. 이는 세션이 없어야 한다는 것을 의미한다. REST가 이런 생각을 한 주된 이유중 하나는 B의 위와같은 고민을 해결해주고 싶었던 것이 아닐까. 실제로 서버가 사용자상태를 항상 알고있지 않아도 된다면 생기는 이득은 생각보다 크다. 세션관리자체가 필요없기 때문에 따라서 별도의 세션스토리지를 관리할 필요도 없고, 사용자가 아무리 많더라도 서버를 분산하여 처리할 때 서버간의 의존관계를 끊어줌으로서 확장성을 꾀할 수 있게 된다.

Stateless가 지닌 장점

  • 세션스토리지 필요없음
  • 서버 분산시 확장성

Stateless Authentication

B는 REST가 주장하는 이러한 장점을 인증에도 적용해보기로 하였다. 그렇다면 세션이 해왔던 일을 Stateless로 어떻게 해결할까? 서버는 이미 인증된 사용자에 대해 매번 인증을 요구하지 않기 위해 세션스토리지를 별도로 사용했었다. Stateless는 이러한 역할을 생략하지는 않는다. 다만 사용자에 관한 상태정보를 서버가 아닌 브라우저에게 책임을 위임한다. 즉 인증시 서버가 가지고 있었을 세션정보를 사용자에게 전송하고 브라우저가 이를 유지하는 것이다. 이것이 바로 Stateless Authentication이다. 개념은 이러한데, 실질적으로 적용해보려고 하니 문제가 있다. 바로 보안이다. 사용자에게 민감한 정보를 서버가 아닌 브라우저에 전송하려다 보니, 서버에서 내부적으로 사용해야 할 정보가 사용자에게 노출되고, 만약 해커에 의해 트래픽을 감청당해 이 값이 노출되기라도 하는 날에는 속수무책으로 내 신변 정보가 팔려나갈 것이다.

JWT

Stateless Authentication 이라는 추상적인 개념을 실제로 구현하는 방법으로는 다양한 방법이 있을 수 있는데, 이중 대표적인 방법이 토큰방식중 하나인 JWT(Json Web Token)을 이용하는 것이다. JWT의 큰 장점은 바로 가벼움이다. 사용자에 관한 정보를 프로퍼티 형태로 관리하는 것을 Claim 방식이라고 한다. JWT는 이를 위해 JSON를 사용하는 것이다. 더불어 JWT는 이를 암호화하여 변조를 방지할 수 있는 장치를 추가한 형태로 제공한다. 이를 위해 JWT에서는 사용자 정보 JSON 원문을 Base64로 인코딩하고, 해당 값에 대해 해싱함수를 통한 해쉬값을 구한다. 이 값은 서버만 알고있는 비밀 키를 가지고 암호화되어 해당 서명이 JWT원문 뒤에 붙게된다. 따라서 사용자 상태 정보값을 변조하더라도 서버에서는 비밀 키로 암호화된 서명값에서 얻어낸 해시값과 원문 비교를 통해 변조여부를 알아낼 수 있다. 이때 비밀 키로 암호화 하는 과정에서 어떤 서명 알고리즘이 사용되었는지가 별도로 JSON방식으로 정의되고 이 또한 Base64로 인코딩하여 이번엔 JWT원문 앞에 붙게된다. 이렇게 만들어진 전체 문자열이 바로 JWT의 전문이 된다.

이렇게 만들어진 JWT는 서버로부터 전송받은 이후 브라우저가 관리하며 HTTP Header에 Cookie나 별도 커스텀 헤더로 추가되어 전송되게 된다. 이는 매번 요청시 서버에 전송하게 되고 서버는 이 토큰을 복호화하여 사용자를 식별하게된다. 그 과정에서 서버가 별도로 사용자 상태정보를 어딘가에 보관하고 조회할 필요가 없기 때문에 세션스토리지나 별도의 데이터베이스 조회가 일어나지 않아도 된다. 이러한 구조는 오늘날 Microservice Architecture와 궁합이 매우 잘맞는다.

Social + Stateless Authentication

현재 개발중인 Memento Magazine 서비스에서는 소셜 로그인 기능을 제공한다. 소셜 로그인 기능은 벤더가 제공하는 OAuth 2.0 인증 API를 통해 사용자의 리소스에 접근할 수 있도록 해주는 Access Token를 발급받는다. 이 토큰을 통해 서비스에서는 사용자의 소셜 데이터를 활용한다. 서비스 자체 로그인없이 SNS의 로그인 기능을 이용하게 된다면 이전에 논했던 JWT를 통한 Stateless Authenticaion과정은 어떻게 되는것일까? 큰 문제없이 병합이 가능하다. 소셜 벤더로부터 최초 사용자 인증시 발급받았던 Access Token과 Refresh Token를 로그인 완료시 JWT에 포함시켜 발급하면 된다. 다만 여기서 한가지 고려할 점은, JWT가 변조방지는 가능하지만 사용자 정보 본문은 충분히 Decode하여 확인할 수 있다는 점이다. Access Token과 같은 민감한 정보를 담고있는데 언제든지 Decode하여 확인할 수 있다는 것은 매우 위험하다. 이를 위해 JWT를 약간 변형하여, 사용자 정보 원문또한 비밀 키로 암호화 하여 저장하였다. 그 결과, 사용자는 자신의 SNS에 접속하기 위한 Access Token과 Refresh Token를 암호화된 JWT로 들고있고 서버는 이를 복호화하여 사용자에게 맞는 SNS에 접속하여 데이터를 처리하면 될 것이다. 그 과정에서 Access Token이 만료되어 문제가 발생했을 시에만 Refresh Token를 통해 Access Token를 재발급해주면 되는 일이다. 만일 사용자 요청시마다 데이터베이스 조회를 통해 Access Token를 가져온다면 이는 완벽하게 Stateless 한 구조가 아니게된다. Stateless의 핵심은 상태를 저장하기 위한 별도의 미들웨어에 의존하지 않음으로써 얻게되는 간결함과 확장성이다. 따라서 어떠한 상황에서라도 사용자 상태를 외부에 요구해서는 안된다. 그런일이 발생하는 순간 Stateless하지 않게 되는 것이다. 상태정보가 너무 방대거나 관계가 복잡하여 Token방식으로 활용하는 것이 부자연스러울 때에는 JWT만으로 완전히 Stateless 한 처리가 불가능한 경우도 존재할 수 있다. 그럴 경우에 토큰이 아닌 다른 방식을 차용하거나 불가피할때에는 어느정도만 개념을 차용하여 부분적으로 득을 보고 나머지 부분은 기존 방식을 같이 이용하는 것도 한가지 방법이 될 것이다.