Search
Duplicate
🥗

로그인 에피타이저

간단소개
어렵고 복잡한 로그인, 개념 맛보기
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
개발지식
Web
Scrap
태그
기초지식
보안
개발지식
9 more properties
흔하디 흔한 게시판 만들기에도 필요한 로그인 기능. 모두가 굉장히 당연하게 생각하는 기능이지만, 막상 구현하려면 막막하고 까다롭습니다. 더군다나 실제 운영되는 서비스라면… 보안에 더욱 신경써야겠죠?
자, 은하수를 여행하는 히치하이커 답게 우리는 일단 로그인, 회원가입 기능을 구현한다라는 목적을 가진 채 정보를 수집하기 시작합니다. 아이디, 비밀번호를 통한 로그인, OAuth, 소셜로그인, 세션, JWT, 암호화 등등 수많은 정보들이 쏟아져 나옵니다. 일단 누군가 쓴 블로그를 따라하다보면 뭔가 되긴 합니다. 하지만, 우린 제대로 이해하고 있는게 맞을까요? jwt가 좋다니까 jwt를 써야겠다, 그럼 비밀번호가 필요없겠지? 필요없을까요? jwt는 비밀번호를 대체할 수 있을까요?
지금까지 당연하게만 써왔던 로그인 기능, 그 안에 숨겨진 수많은 정보들. 로그인이라는 메인 요리를 맛보기 전에, 몇가지 개념들을 먼저 맛보며 차근차근 먹어봅시다. 우선 시작은 일반적인 서버와 클라이언트 간의 네트워크 통신 과정부터 시작합니다.

HTTP

네트워크에 대한 깊은 이해는 다루지 않겠습니다. 다만, OSI 7계층의 최상위, 애플리케이션 레벨에서의 통신방식에 대해서는 약간의 이해가 필요합니다. 일반적으로 웹브라우저 상단에는 https://intra.42.fr 과 같은 도메인 주소가 있습니다. 직접 이 주소를 입력하지 않더라도 구글이나 네이버 등을 통해 링크를 클릭하면 해당 주소로 이동해 브라우저가 페이지를 렌더링합니다. 이 때, 개발자 도구를 열어 네트워크 탭을 열어보면 수많은 요청이 오가는 걸 볼 수 있습니다. 이용자가 보게 될 화면을 그리기 위해서 네트워크 요청을 통해 보여줘야할 정보를 서버로부터 가져옵니다. 이 때, 도메인 주소에서 볼 수 있듯 주로 HTTP를 이용합니다. 몇가지 특징만 추려 간단하게 설명하자면, 일회성 무상태 단방향 요청입니다. 요청이 1회 수행되고, 클라이언트와 서버는 서로의 상태를 알지 못하며, 클라이언트에서 요청하지 않는 이상 서버는 응답을 보낼 수 없습니다. 중요한 부분은 무상태(Stateless)입니다. 서로의 상태를 알 수 없기 때문에 요청하는 클라이언트가 누구인지, 요청을 처리해도 되는지 판단할 근거가 별도로 필요합니다. 즉, 클라이언트는 서버에 요청을 할 때 본인이 누구인지도 요청에 담아 보내야한다는 뜻입니다.
자, 그렇다면 어떻게 요청을 하면 될까요? 매번 내가 누구인지, 내가 그 사람이 맞는지 확인시켜줘야 할까요? 간단하게 예를 들어봅시다. 저희는 피신을 통과해 42의 카뎃이 되었고, 클러스터를 포함해 42의 여러 공간과 서비스를 이용할 수 있게되었습니다. 하지만, 클러스터는 허가없이 외부인이 출입할 수 없습니다. 들어가려면 카뎃임을 증명해야합니다. 이 때, 스태프분께 인트라 아이디를 말씀 드리고, 등록된 사진과 여러분의 얼굴을 비교해 입장을 처리한다고 생각해봅시다. 얼마나 불편할까요? 뭐, 한번 들어가면 이후엔 이런 번거로움이 없으니 그럴 수 있다고 칩니다. 그럼 조건을 한가지 더해보겠습니다. 특정 공간에 들어가려면, 예를 들어 특정 공간에는 멤버만 들어갈 수 있다면, 인트라 아이디를 말해주고 사진을 비교해 본인확인을 하고, 멤버인지도 확인해야 합니다. 잠깐 화장실을 가기 위해 나왔다가 다시 들어가려면 또 이 과정을 거쳐야 하겠죠? 얼마나 불편할까요? 이런 불편을 해소하기 위해 서버는 인증과 인가. 두가지 단계로 구분해서 클라이언트를 검증합니다.

인증(Authorization)

앞의 예시에서 이어 설명해보겠습니다. 저는 hwayu이고, 제가 본인이 맞다는걸 증명했습니다. 한 번 증명했으면, 이후 다시 증명할 필요가 없도록 카드를 발급해줍니다. 저는 이제 이 카드를 통해 클러스터를 쉽게 드나들고, 권한에 맞는 공간에 출입할 수 있게 되었습니다. 이렇듯 카드를 발급받는 과정을 인증과정이라고 볼 수 있습니다. 클라이언트는 아이디와 비밀번호를 입력해 서버에 등록된 유저라는 것을 인증한 뒤, 서버로부터 식별 도구를 발급받습니다. 이제 이 식별 도구를 이용하면 서버는 인증을 처리했기 때문에 불필요한 인증절차를 거치지 않고 바로 요청을 처리할 수 있습니다. 세션과 JWT는 바로 이 식별 도구입니다. 앞서 42 카드의 예로 들어보자면, 세션방식은 카드에 들어있는 정보가 카드 번호 뿐입니다. 나머지 인증 정보는 모두 서버에서 보관하고, 카드를 태그하면 카드번호를 이용해 이 카드의 인증정보를 조회한 뒤, 인증정보를 이용해 제 정보에 접근할 것입니다. JWT 방식은 카드에 저에 대한 인증정보가 담겨있는 것으로 볼 수 있습니다. 카드를 발급 받을 때, 카드 안에 저에 대한 여러가지 정보를 저장해서 줍니다. 예를 들어, 인트라 아이디, 마지막 출입시간, 블랙홀, 평가포인트 등등, 여러가지 정보를 담을 수 있습니다. 카드를 태그하면 별도의 조회 없이 카드에 담긴 정보만 읽으면 되는 방식이죠. 더 자세한 내용은 이 글의 목적에 벗어나기 때문에 생략하겠습니다.

인가(Authentication)

자, 이제 저희는 카드만 태그하면 클러스터를 출입할 수 있습니다. 하지만, 클러스터 내부에도 카뎃이 출입할 수 있는 장소가 제한되어있듯, 클러스터로 들어왔더라도 어떤 권한을 갖고 있는지에 따라 접근 가능한 공간이 나뉘어져있고, 공간 입장 전에 권한을 확인하는 과정이 필요합니다. 이렇게 권한을 확인하는 과정이 인가과정입니다. 카드를 소유함으로써 인증절차는 생략하고, 카드를 통해 저의 정보에 접근해 러너인지, 멤버인지, 보컬인지 확인하고 권한에 따라 문이 열릴 수도, 거부당할 수도 있습니다. 이러한 인가를 위해서는 인증 절차가 선행되어야 합니다. 누구인지도 모르는데 권한을 판단할 수는 없잖아요? 다만 앞서 설명한 것 처럼, 인증을 한 번 받으면 인증과정을 생략하도록 별도의 식별도구가 있기 때문에 요청에 이 식별도구만 담아보내면 바로 인가과정을 거칠 수 있습니다.

생각해볼 점

누군가 카드를 훔쳐간다면?

서버의 로그인은 앞선 클러스터 출입의 예시와 같습니다. 카드를 발급받아 클라이언트(일반적으로 브라우저)에 저장하고 서버에 요청을 보낼때마다 적절한 방식으로 카드를 같이 보내면 서버에서 카드를 확인하고 적절한 응답을 보낼 수 있죠. 그런데 만약, 누군가가 카드를 훔친다면 어떻게 될까요? 앞서 말씀드린 것처럼, 카드를 이용하면 더이상 인증과정을 거치지 않습니다. 즉, 한번 발급받은 카드가 곧 저를 인증하는 수단인 것인데, 이것을 도난당했으니 카드를 훔친사람은 저인척하고 클러스터를 돌아다니게 되는 셈이죠. 실물 카드는 일단 주면 관리하는 것은 발급받은 저희의 몫이라지만, 웹에서는 전적으로 개발자의 몫입니다. 이 카드를 어떻게 보관해서 요청에 사용할지, 어떻게 하면 더 안전하게 보관할 수 있을지 등을 생각해야하죠.
또 한가지는, 카드를 분실했을 경우입니다. 세션과 jwt는 인가 방법에도 차이가 있지만 탈취당했을 때 처리를 어떻게 할것인가에서도 다릅니다. 세션방식은 카드에 담겨있는 정보가 카드 번호 뿐이기 때문에, 분실신고를 했을 때, 해당 카드번호를 무효화 시켜버리면 됩니다. 하지만, jwt에는 저의 모든 인증정보가 들어있기 때문에 따로 조치를 취할 방법이 없습니다. 최대한 잃어버리지 않도록 주의하면서도 잃어버렸을 때 어떻게 처리할지도 생각해야하죠.

다른 누군가가 나를 사칭한다면?

마지막으로, 어떻게 인증할 것인가도 생각해봐야할 점입니다. 앞선 예시에서 저를 인증하기 위해 인트라 아이디와 프로필 사진을 이용한다고 예시를 들었습니다. 누군가 제 사진을 가지고 가면을 만들어 쓰고 저인척 한다면 어떻게 될까요? 저라고 생각하고 카드를 발급해주겠죠. 비밀번호 역시 동일합니다. 비밀번호가 노출되면, 다른 누군가가 아이디와 비밀번호를 이용해 저인척 하고 인증을 받겠죠. 일반적으로 인증 규칙은 서버에 아이디가 있고, 그 아이디의 비밀번호가 입력값과 일치한다라는 두가지 뿐이니까요. 그리고 제 인트라에 들어가서 자퇴처리라도 해버린다면… 월렛을 마구 쓰고 저도 모르게 평가를 잔뜩 열어버린다면…. 끔찍하죠?
이런 사태를 방지하기 위해 서버는 클라이언트로부터 전달받은 비밀번호를 바로 암호화하여 저장하거나, 혹은 비교합니다. 만약 노출되어도 원문을 알아볼 수 없도록이요. 저장된 비밀번호는 이미 암호화를 거쳤고, 이 암호코드를 통해 요청을 한다해도 서버는 이 암호코드를 다시 암호화할 것이고, 결과가 다를테니 인증을 안해줄겁니다.
하지만 여전히 비밀번호를 저장해 관리하는 것에는 위험성이 있습니다. 그래서 최근에는 신뢰할만한 서비스에 인증을 맡기는 OAuth방식이 많이 사용됩니다. 앞선 예시로 돌아가면 제 인트라 아이디와 얼굴을 보여주는 대신, 저는 핸드폰에서 카카오톡을 켜고 QR코드를 만들어 보여줍니다. 이 QR이 바로 카카오에서 대신 인증을 처리해준 결과입니다. 이 결과를 저장해 사용하면 비밀번호를 저장하지 않아도 되고 유출되어도 위험성이 덜합니다. QR을 통해 인증을 처리하는 과정이 있기 때문에 인증 결과를 알고 있다고 해도 그것만으로는 할 수 있는게 없죠.

그래서

로그인 기능의 구현은 어렵습니다. 개발자로서 사용자의 정보를 쉽고 빠르게 저장하고 복잡한 비즈니스 로직에 활용할 수 있도록 하면서 동시에 사용자의 보안도 신경써야 하기 때문이죠. 다른 기능도 그렇겠지만 로그인 기능 역시 다양한 선택지와 각각의 장단점이 있고, 정답은 없지만 오답은 있는 어려운 문제입니다. 이 난해한 퍼즐을 한번에 풀자면 굉장히 어렵겠지만, 이렇게 전반적인 과정과 이유, 흐름에 대해 이해한다면 보다 쉽게 해결해나갈 수 있습니다.
슬랙에서의 질문에 답변을 드리고 팔만코딩경에 로그인 관련 글을 찾아보다가 로그인 방식이나, 세션과 토큰 등에 대한 설명은 있어도 인증과 인가의 개념에 대해서는 다룬 글이 없어 이렇게 글을 써보았습니다. 최대한 쉽고 빠르게 이해할 수 있도록 예시를 들기도 했고, 지금까지 제가 이해한 대로 설명한 글이기에 실제와는 다를 수 있습니다. 이를 바탕으로 간단하게 개념 스케치만 하시고 더 자세한 정보는 따로 찾아보시는걸 추천합니다. 제목처럼 에피타이저 정도로만 봐주시기바랍니다.