맨 땅에 프론트엔드 개발자 되기

브라우저의 인증(로그인) 방식에 대해 알아보자 Session / JWT / OAuth 2.0 본문

코딩 공부 일지/Browser & Network

브라우저의 인증(로그인) 방식에 대해 알아보자 Session / JWT / OAuth 2.0

헬로코딩 2022. 6. 28. 12:50
728x90

HTTP 프로토콜의 stateless 특성과 쿠키

HTTP를 이용한 통신 프로토콜은 stateless의 특성을 가지고 있기 때문에, 페이지에서 링크를 타고 다른 페이지로 이동할 때 상태가 유지되지 않는다. 여기서 상태라는 것은 정보의 유지를 말하는데 예를 들어, 로그인을 했을 때 정보가 유지되거나 쇼핑몰 사이트에서 장바구니에 물건을 담은 정보가 유지되는 것을 말한다. stateless 특성은 빠르게 원하는 정보만 요청해서 받아올 수 있도록 고안된 것이기 때문에 이를 보완하기 위해 쿠키와 같은 기술이 도입되었다.

 

웹 브라우저 쿠키에 대해 알아보자

쿠키의 탄생 배경 HTTP는 상태를 계속 유지하지 않는 스테이트리스(Stateless)프로토콜이다. 서버와 클라이언트 간에 리퀘스트와 리스폰스를 교환하는 동안에 상태를 관리하지 않기 때문에 이전에

babycoder05.tistory.com

세션과 쿠키를 이용한 인증

  1. 사용자가 사용자 정보로 로그인을 한다.
  2. 입력된 계정 정보를 서버로 보내 서버에 저장된 계정 정보와 일치함을 확인하면, 사용자에게 고유한 ID 값을 부여해서 세션 저장소에 저장한 후, 이에 대한 Session ID를 발급한다.
  3. 서버는 응답으로 Session ID를 세션 쿠키에 담아 사용자의 브라우저로 보낸다. 이후 매 페이지 요청마다 HTTP 요청 헤더에 Session ID가 담긴 쿠키를 같이 보낸다.
  4. 서버는 쿠키를 받아 세션 저장소에서 대조를 한 후 사용자에 맞는 데이터를 보내준다.
  5. 세션이 종료되면 Session ID와 유효성이 종료된다.

- 장점

  • 계정 정보를 서버에서 확인하고 그에 대해 발급된 Session ID는 유의미한 정보를 가지고 있지 않으므로 HTTP 요청 중에 Session ID가 노출되더라도 사용자의 정보를 가지고 있지 않으므로 안전하다. (추가: 그러나 Session ID만 식별자로 사용해서 모든 get, post 요청에 대한 접근 권한이 생긴다면 위험하다.)
  • 매 HTTP 요청마다 사용자의 정보 대신 Session ID를 확인하므로 상대적으로 서버가 가벼운 확인 작업을 하게 되어 서버 자원 접근에 용이하다.

- 단점

  • 세션 유지 중에 해커가 쿠키를 탈취한 후 그 쿠키를 이용해 HTTP 요청을 보내면 서버는 사용자로 오인해 정보를 전달할 수 있다. 이를 세션 하이재킹 공격이라고 한다. 해결책으로는 HTTPS 프로토콜 사용세션 만료 시간을 지정해주는 것이다.
  • 서버에서 세션 저장소를 위한 추가 저장공간을 필요로 한다. (서버 비용 증가)
  • 매 HTTP 요청마다 사용자의 정보 대신 Session ID를 확인하므로 상대적으로 가볍긴 하지만 그래도 여전히 서버 DB에서 Session ID를 조회해야 한다.

Access Token을 이용한 인증

  1. 유저가 사용자 정보로 로그인을 한다.
  2. 서버 측에서 해당 사용자 정보를 검증한다.
  3. 사용자 정보가 서버에 저장된 정보와 일치하면 유저에게 signed 토큰을 발급해준다. (signed 토큰이란 서버에서 정상적으로 발급된 토큰임을 증명하는 signature를 가진 토큰을 의미한다.)
  4. 유저 측에 토큰을 저장하고, 서버에 HTTP 요청을 할 때 해당 토큰을 함께 서버에 전달한다.
  5. 서버는 토큰을 검증하고, 요청에 응답한다. (서버 쪽에 세션 저장소와 같은 저장소가 필요하지 않고 토큰 검증 로직만 가지고 있게 된다.)

- 토큰의 장점

  • Session ID를 통한 로그인을 할 때는 유저가 처음 로그인 요청을 보냈던 서버에만 지속적으로 통신을 하도록 설정해야 한다. 그러나 유저가 늘어나고 서비스가 커질 수록 서버량도 증설이 되어야 하는데, 서버의 확장성(scalability)이 떨어질 수밖에 없다. 토큰을 이용하면 세션 저장소가 필요 없으므로 서버 확장성이 커지게 된다.
  • 토큰을 이용한 방식으로 로그인을 구성하면, 토큰만 발급하면 되기 때문에 Facebook 로그인, Google 로그인과 같은 소셜 로그인 기능을 제공할 수 있다. 
  • JWT 방식이 웹 표준으로 채택되어, C, Java, Python, C++, R, C#, PHP, JavaScript, Ruby, Go, Swift 등 수많은 프로그래밍 언어에서 지원된다.

- 토큰의 단점

  • 토큰이 탈취 당하면 만료될 때까지 대처가 불가능하다. 세션을 이용한 방식일 때는 Session ID가 탈취되었다고 인지를 하면 세션 저장소에서 그 값을 지워 연결을 끊을 수 있었지만, 토큰 방식은 토큰 발급 이후의 상황에는 유저가 토큰을 관리하는 형태이므로, 탈취 상황이 되어도 서버 쪽에서 관리할 방법이 없다. 이를 보완하기 위해 토큰의 만료 기간을 짧게 가져가기도 한다. 그러나 이 방법도 30분, 1시간 등 토큰 만료 시간이 너무 짧으면 사용자가 불편함을 느낄 수도 있다. 그래서 나온 방식이 Refresh Token을 발급하는 방식이다. 
  • Access Token이 만료될 때마다 새롭게 발급해야 하므로 서버의 자원 낭비가 생긴다.

- JWT

JWT(JSON Web Token)는 인증에 필요한 정보들을 Token에 담아 암호화시켜 사용하는 서명된 토큰이다. JWT는 토큰을 이용한 인증 방식에서 가장 많이 사용되고 있고, Header, Payload, Signature가 점(.)으로 연결된 구조를 가지고 있다. JSON 객체를 이용하기 때문에 가볍다.

  • 헤더(Header): 헤더에는 토큰의 타입이나, 서명 생성에 어떤 알고리즘이 사용되었는지 저장한다.
{
  "typ": "JWT",
  "alg": "HS256"
}
  • 정보(Payload): 토큰 발급자, 토큰 제목, 토큰 대상자, 토큰 만료 시간, 토큰 활성 날짜, 토큰 발급 시간, JWT 토큰 식별자 등 토큰에 대한 정보를 담는다. 중요한 것은 아이디나 비밀번호 같은 민감 정보는 payload에 담지 않는다. 단순 JSON 객체이기 때문에 디코딩하여 누구나 정보에 접근할 수 있다.
{
    "sub": "1",
    "iss": "ori",
    "exp": 1636989718,
    "iat": 1636987918
}
  • 서명(Signature): JWT에서 가장 중요한 부분인 서명은 서버가 가지고 있는 개인키로만 복호화될 수 있다. 이 서명은 Base64 방식으로 인코딩한 header, payload 그리고 서버의 개인키를 더한 후 서명되기 때문에 만약 header와 payload가 변조된 상태로 요청을 하면 서버가 발급했던 signatured 안의 payload와 다르기 때문에 인증이 불가능해진다. 

- Refresh Token

토큰 방식의 단점을 보완하기 위해 등장한 것으로 JWT를 처음 발급할 때 Access Token과 함께 Refresh Token을 함께 발급하여 토큰의 짧은 만료 시간을 해결한다. Refresh Token은 말 그대로 Access Token이 만료되었을 때 새로 발급받게 해주는 것을 보장하는 토큰이다. 

  1. 사용자는 로그인 시에 Access Token과 Refresh Token을 함께 발급받고 사용자 측에 저장한다.
  2. Access Token이 만료가 되고 서버로 부터 만료되었다는 메시지를 받으면 사용자 측에 저장된 Refresh Token으로 새로운 Access Token 발급을 요청한다.
  3. 서버는 서버 쪽에 있는 Refresh Token 저장소에서 사용자 측에서 받은 Refresh Token과 일치하는 것이 있는지 확인하고 새로운 Access Token을 생성해서 전달한다.

이 방식은 세션 저장소를 이용한 방식과 유사하다. 그래서 세션 저장소가 서버에 필요한 것처럼 Refresh Token 저장소가 필요한 단점을 가지고 있다. 그러나 매 HTTP 통신마다 Session ID를 확인해야 하는 작업이 필요 없고, 서버 확장성이 여전히 유지되며, 토큰이 탈취됨을 인지하면 Refresh Token을 삭제할 수 있는 부가 옵션이 생긴다.

OAuth 2.0을 이용한 인증

OAuth는 외부 서비스의 인증 및 권한 부여를 관리하는 범용적인 프로토콜로 현재 범용적으로 사용되는 것은 OAuth 2.0이다. 여기서 외부 서비스가 등장하는데, 많은 서비스들이 카카오 로그인, 네이버 로그인, 구글 로그인과 같은 소셜 로그인 방식을 이용하고 있다. 그러므로 여기서 사용자(나), 서비스(클라이언트), 로그인 기능 제공자(서버) 3개의 주체가 등장하게 된다. 

  1. 사용자가 서비스(클라이언트)에 인증 요청을 한다.
  2. 클라이언트는 사용자에 인증할 수단(카카오, 네이버, 구글 로그인 url)을 보낸다.
  3. 사용자가 인증에 통과하면 로그인 기능 제공자는 클라이언트의 유저가 맞는 지 확인 후 Access Token, Refresh Token, 그리고 유저의 정보를 발급해준다. 클라이언트의 유저가 아니라면 회원가입을 진행한다.
  4. 클라이언트는 Access Token을 클라이언트 DB에 저장하거나 사용자에게 넘긴다.
  5. 사용자가 통신을 하면서 로그인 기능 제공자가 가지고 있는 자원이 필요하게 되면 클라이언트는 Access Token을 담아 로그인 기능 제공자에 요청한다.
  6. 로그인 기능 제공자는 Access Token이 유효한 지 확인 후 클라이언트에 자원을 보낸다.
  7. 만일 Access Token이 만료됐거나 위조되었다면, 클라이언트는 Refresh Token을 로그인 기능 제공자에 보내 Access Token을 재발급 받는다.

- 참고사항

만약 내가 서비스를 만들고 OAuth를 이용해서 유저들의 인증을 처리하고 싶다면, 사전에 로그인 기능을 제공하는 측에 등록하는 과정이 필요하다. 개발자가 등록하고자 하는 서비스의 웹 어플리케이션을 등록한 뒤 APP_ID와 CLIENT_ID 등을 보내야 OAuth를 제공하는 측에서 어느 서비스인지를 알 수 있다.

728x90