카카오 로그인, 프론트와 백엔드 어디서 구현해야 할까?

카카오 로그인, 프론트와 백엔드 어디서 구현해야 할까?

Category
Published
September 7, 2024
Last updated
Last updated September 7, 2024
카카오 소셜 로그인등의 OAuth 인증을 백엔드 서버와 프론트엔드(앱, 웹)를 모두 가지는 서비스에서 구현해보며 고민 해본 내용들을 공유합니다.

용어 정리

우선 글을 시작하기 전에 간단히 헷갈리기 쉬운 용어에 대해 정리하자.
OAuth에 있어서
  • Provider란, 기존 유저 정보를 가지고 외부 앱에 소셜 로그인을 제공하는, 즉 카카오나 네이버등을 의미한다.
  • Client란, provider에게 인증 관련 서비스를 요청하는, 즉 서비스를 개발하는 우리들을 의미한다.
  • User또는 Resource Owner는 우리 서비스를 사용하는 일반 사용자를 의미한다.
 
추가적으로 자주 보게될 여러 값들에 대해 잠깐 알아보자
  • Client ID : 클라이언트 웹 어플리케이션을 구별할 수 있는 식별자이며, 노출해도 무방하다.
  • Client Secret : Client ID에 대한 비밀키로서, 절대 노출해서는 안된다(카카오 로그인에서는 선택).
  • Authorized redirect URL : Authorization Code를 전달받을 리다이렉트 주소.

카카오 소셜 로그인

카카오 소셜 로그인은 웹을 기준으로 대게 아래와 같은 방식으로 이루어진다(다른 OAuth 인증도 대게 이와 비슷한 과정을 따른다).
  1. 유저가 서비스에 로그인을 하기 위해 <로그인 버튼>을 누르면, client는 provider의 서비스 페이지로 user를 이동(리다이렉트 등) 시켜준다.
    1. 이때 보통 이동되는 url에는 client_id와 redirect_uri가 포함된다. 어떤 client(우리의 서비스)인지 확인하기 위해 client_id를 사용하고, 로그인 완료후 user 정보를 client에 보내주기 위해 redirect_uri를 입력받는다.
    2. 이때 악의적인 요청을 방지하기 위해서 redirect_uri로 지정할 수 있는 uri는 화이트리스트 방식으로 지정해줘야 한다.
  1. User는 provider의 서비스에서 로그인을 진행하고, 로그인을 완료하면 code를 포함하여 redirect_uri로 리다이렉트 된다.
    1. 즉 다음과 같은 uri로 리다이렉트 된다: https://<redirect_uri>?code=<code>
  1. client는 파라메터로 들어온 code값을 받아와 provider에 client_id, redirect_uri 그리고 code값을 전송해 user의 access_token, refresh_token를 받는다.
  1. Client는 이렇게 받은 token을 가지고 provider에 요청을 보내 user의 정보를 확보할 수 있다. 해당 정보를 통해 client 자신의 서비스에서 추가적인 절차를 걸쳐 회원가입/로그인을 완료하면 된다.
앱 로그인에서는 SDK를 이용하면 중간절차가 알아서 진행되고, 최종적으로 token을 받을 수 있는 방식이다.

보안적으로 문제는 없을까?

이전과 달리 요즘의 웹 개발 행태는 프론트엔드와 백엔드가 나누어져 있는 방식이다. 즉 이제는 프론트엔드에서 code나 access_token을 취득하고 이를 어떤 방식으로 백엔드와 안전하게 소통할지 고민을 할 필요가 생겼다.
추가적으로 앱 서비스 역시도 앱과 서버가 나누어져 있는 형태이기 때문에 동일한 문제를 생각해보게 된다.
앞으로 글에서 웹 프론트엔드와 앱을 ‘클라이언트’, 백엔드 서버를 ‘서버’로 용어를 통일하겠다.
우선 클라이언트에서 전체 카카오 로그인 처리를 완료한다는 가정하에 클라이언트는 access token을 받게 된다.
이때 유저 자신을 증명하는 용도로 access token을 서버로 보내면 서버는 해당 access token을 provider인 카카오 인증서버에 보내 유효한지 확인 후 사용자 로그인 절차를 거칠 수 있다.
그러나 이때 access token은 탈취되면 안 되는 매우 민감한 데이터이기에 이를 클라이언트에서 서버로 전송한다면 보안적으로 문제(ssl 미사용 시 더더욱)가 생길 수 있다.

문제를 해결할 방법

위와같은 보안 문제를 해결하기 위한 여러 가지 방안을 찾아보았다.

Code를 이용하는 방법

24.02.02 글 추가
앱 SDK가 아닌, REST API를 사용한다면 access token을 받기전에 code(authorization code)를 받는 절차를 거친다. 이때 code를 이용해 provider와 통신 해 로그인을 완료하고 access token을 클라이언트에서 서버로 보내는 것이 아닌, 중간 과정중 code를 서버로 보내는 방식이다.
이 방식의 장점은, code는 한 번만 사용되는 값이고 10분 정도 내에 소멸되기에 더 안전하고, 탈취당하더라도 client_secret 값이 없다면 code 만으로 access token을 획득할 수 없기 때문에 더 안전하다(client secret을 사용하도록 설정해두면).
단점으로는 SDK등을 사용할시에 code 절차가 중간에 없고, 최종적으로 access token만을 받기 때문에 사용이 어렵다.
또한 서버에서 code로 access token을 얻기 위해서는 client id와 code 를 발급할 때 사용된 redirect uri가 필요한데, redirect uri를 하나만 사용하지 않는다면 더 복잡한 구현이 필요할 수 있다(code와 redirect uri를 같이 서버로 보내 인증하는 방식 등).

User id를 이용하는 방법

Access token을 provider에서 받는것 까지는 클라이언트에서 JDK 등을 사용해서 처리한다. 이후 사용자 정보에 접근할 수 있는 access token이 아닌 access token을 이용해 얻을 수 있는 유저의 id를 서버로 보내는 것이다.
Id는 어드민 키가 있다면 서버에서 provider 서버를 통해 id가 유효한지 확인할 수 있기 때문이다.
이때 유저 id는 access token을 이용해서 카카오 인증서버의 '사용자 정보 가져오기' 기능을 통해 얻을 수 있다.이런 방식의 장점은 바로 외부인은 유저 id 자체로는 아무 정보도 알아낼 수 없다는 것이다. 유저 id를 취득하여도 이를 어드민 키와 조합하여야만 유저 정보를 확인할 수 있기 때문이다.
그러나 유저 id를 이용하는 해당 인증 방식을 선택한다면, 유저 id만을 가지고 서비스에 로그인할 수 있게 된다는 점이 된다. Id가 유출된다면 id는 변경되지 않는 값이기 때문에 영구적으로 해킹을 당할 수 있다. 그렇기에 단순히 id만을 이용해 인증을 하기에는 문제가 있어 보인다.
특히 이것이 정상적으로 앱에서 보내온 요청인지 악의적인 조작된 요청인지 구분할 수 없다. 이를 방지하기 위하여 번들ID나 키 해시등의 방법을 이용해서 임의요청을 방지해 줄 수 있다.
결론적으로 user id만을 이용하는 인증방식은 매우 위험해 일반적으로 사용하기에는 문제가 있다.

Access token을 이용하는 방법

실제로 여러 인터넷의 글을 보아도 access token을 이용하는 경우가 많고, Django의 allauth 라이브러리를 봐도 바로 access token을 전달받는 방식으로 구현되어 있었다.
가능하다면 code를 이용하는 것이 좋겠으나, 여러 문제로 인해 대부분 access token을 그냥 전송하는 방식을 사용하는 것 같다. 이때 access token을 이용해서 인증을 완료 한 후에는 최대한 빠르게 폐기해버리는 것을 권장한다.

레퍼런스