요청 하나하나가 서로를 기억하지 못한 채 독립적으로 오간다는 성질은, 편에서 편으로 이어지며 여러 번 짚어온 주제다. 그런데 이 무상태라는 성질은 편리한 만큼 하나의 숙제를 남긴다. 매 요청이 앞을 기억하지 못한다면, 지금 이 요청을 보낸 사람이 누구인지를 서버는 대체 어떻게 아느냐는 것이다. 나는 인증이라는 주제를 이 질문의 답으로 이해하고 나서야, 왜 요청마다 신원을 증명하는 절차가 되풀이되는지를 납득했다.
이번 편은 인증의 세부 기술을 파고들기 전에, 그 큰 그림을 먼저 그리는 자리다. 상대가 누구인지 확인하는 인증과 그가 무엇을 할 수 있는지 정하는 인가가 어떻게 다른지, 무상태 통신에서 신원을 이어가려면 왜 매 요청이 자신을 증명해야 하는지, 그리고 그 증명을 실어 나르는 대표적인 방식들이 어떤 성질로 갈리는지를 살펴본다. 세부 흐름은 뒤의 인증 편들에서 따로 깊이 다루므로, 여기서는 갈래와 원리에 집중한다.
인증과 인가는 다른 질문이다
인증을 이야기할 때 가장 먼저 갈라두어야 할 것이 인증과 인가의 구분이다. 인증은 당신이 누구인가라는 질문에 답하는 절차이고, 인가는 그 신원이 이 일을 해도 되는가라는 질문에 답하는 절차다. 두 낱말이 비슷해 뭉뚱그려지곤 하지만, 이 둘은 다루는 물음이 다르다. 나는 신원을 확인하는 일과 권한을 판단하는 일을 머릿속에서 늘 별개의 단계로 떼어둔다.
순서로 보면 인증이 먼저고 인가가 나중이다. 누구인지 확인되지 않은 상대에게는 무엇을 해도 되는지 따질 것도 없다. 신원이 밝혀진 뒤에야, 그 신원이 이 자원에 손댈 자격이 있는지를 판단하는 단계로 넘어간다. 확인되지 않은 요청을 막는 것과 확인은 되었지만 권한이 없는 요청을 막는 것은 그래서 서로 다른 국면이고, 앞 편에서 본 실패 코드도 이 둘을 다른 것으로 나눈다.
이 구분이 흐릿하면 설계가 어긋난다. 신원 확인만 해두고 권한 판단을 빠뜨리면, 로그인만 하면 남의 것에도 손댈 수 있는 구멍이 생긴다. 반대로 권한 판단을 하려는데 신원이 제대로 확인되지 않았다면, 그 판단은 허공에 대고 하는 셈이다. 나는 어떤 자원을 지킬 때, 먼저 이 요청자가 누구인지를 확실히 세운 뒤에 그가 이 일을 해도 되는지를 따지는 두 단계를 항상 나란히 놓는다.
이번 편이 주로 다루는 것은 앞쪽, 곧 신원을 확인하는 인증이다. 무상태 통신에서 요청자의 신원을 어떻게 세우고 이어가느냐가 이 편의 관심사다. 그 신원 위에서 무엇을 허용할지를 정하는 인가는 별개의 큰 주제라, 여기서는 인증이 그 판단의 토대를 놓는다는 점만 짚어둔다. 인증이 부실하면 그 위에 세운 인가도 함께 무너진다는 관계만은 분명히 해두려 한다.
인증을 신원을 세우는 일로 보면, 그 핵심은 상대가 자신이 주장하는 그 사람임을 증명하는 데 있다. 나는 내가 그 사람이라고 말하는 것만으로는 부족하고, 그 주장을 뒷받침할 무언가를 함께 내밀어야 한다. 그 무언가가 비밀번호일 수도, 발급받은 표식일 수도 있다. 인증 방식의 차이는 결국 이 증명거리를 무엇으로 삼고 어떻게 실어 나르느냐의 차이로 좁혀진다.
그래서 나는 어떤 인증 방식을 보든 두 가지를 먼저 묻는다. 무엇으로 신원을 증명하는가, 그리고 그 증거를 어디에 담아 보내는가이다. 이 두 물음의 답을 잡으면 그 방식의 성격이 대체로 드러난다. 세부 절차가 아무리 복잡해 보여도, 그 밑바탕에는 늘 이 증명거리와 그것을 나르는 통로라는 두 축이 있다. 인증의 큰 그림은 이 두 축 위에 그려진다.
무상태 통신이 낳는 인증의 숙제
인증이 까다로운 근본 이유는 통신이 무상태라는 데 있다. 각 요청은 앞선 요청을 기억하지 못한 채 홀로 도착하므로, 한 번 신원을 확인했다고 해서 그 사실이 다음 요청까지 저절로 이어지지 않는다. 문을 한 번 열어주었다고 그 사람이 그다음에 올 때도 알아서 열리는 것이 아니라, 올 때마다 자신을 다시 밝혀야 하는 구조인 것이다. 나는 이 되풀이가 무상태의 필연적 대가라고 본다.
그렇다면 사용자에게 매번 비밀번호를 다시 묻는 것은 답이 될 수 없다. 요청마다 원래의 비밀번호를 실어 보내게 하면, 그 민감한 값이 수없이 반복해 오가며 노출될 위험만 커진다. 그래서 대개는 처음 한 번 제대로 신원을 확인한 뒤, 그 사실을 대신 증명해줄 다른 표식을 발급한다. 이후의 요청들은 원래의 비밀번호 대신 이 표식을 내밀어 자신이 이미 확인된 사람임을 밝힌다.
이 표식이 인증의 핵심 장치다. 처음의 확인 절차는 무겁고 신중하게 한 번만 거치고, 그 뒤로는 가볍고 안전한 표식을 반복해 제시함으로써 신원을 이어간다. 표식은 이 사람이 언제 어떤 자격으로 확인되었는지를 담은 일종의 통행증인 셈이다. 매 요청은 이 통행증을 보여주고, 서버는 그것이 유효한지만 확인하면 된다. 원래의 비밀번호는 첫 관문에서만 쓰이고 그 뒤로는 등장하지 않는다.
이 표식에는 반드시 수명이 있어야 한다. 한번 발급된 통행증이 영원히 유효하다면, 그것이 새어 나갔을 때 막을 방법이 없어진다. 그래서 표식은 정해진 기간이 지나면 힘을 잃도록 하고, 필요하면 다시 발급받게 한다. 나는 이 수명을 편의와 안전 사이의 저울질로 이해한다. 너무 짧으면 자주 다시 받아야 해 번거롭고, 너무 길면 새어 나갔을 때의 위험이 오래간다.
표식을 다룰 때 늘 전제되는 것이 통로의 보호다. 표식이 아무리 잘 설계되어도, 그것이 오가는 통신이 훤히 들여다보이면 도중에 가로채여 도용된다. 그래서 인증 표식이 오가는 통신은 반드시 암호화된 통로 위에서 이루어져야 한다. 나는 인증을 이야기할 때 통신 보호를 늘 한 묶음으로 전제한다. 표식과 통로는 떼어 생각할 수 없는 한 쌍이고, 이 통로 이야기는 뒤의 편에서 따로 다룬다.
정리하면 무상태 통신은 요청마다 신원을 다시 세워야 하는 숙제를 낳고, 그 답으로 첫 확인 뒤 발급한 표식을 반복 제시하는 방식을 쓴다. 표식에는 수명을 두어 위험을 제한하고, 그 표식이 오가는 통로는 반드시 보호한다. 이 큰 얼개를 잡아두면, 뒤에 나올 여러 인증 방식이 결국 이 얼개를 저마다 다르게 구현한 것임을 알아보게 된다. 방식은 여럿이어도 풀려는 숙제는 하나다.
신원을 실어 나르는 방식들
신원 증명을 실어 나르는 방식은 크게 몇 갈래로 나뉜다. 첫째는 서버가 상태를 기억하고 클라이언트에는 그 기억을 가리키는 표를 쥐여주는 방식이다. 서버가 로그인한 사용자의 정보를 자기 쪽에 보관하고, 클라이언트에는 그 보관함을 가리키는 짧은 열쇠만 건넨다. 클라이언트는 요청마다 이 열쇠를 내밀고, 서버는 그것으로 자기 쪽 기억을 찾아 누구인지 알아낸다.
이 방식은 신원의 실체를 서버가 쥐고 있어 통제가 쉽다는 장점이 있다. 어떤 사용자의 접속을 끊고 싶으면 서버 쪽 기억을 지우면 그만이다. 다만 서버가 모든 접속의 상태를 기억해야 하므로, 서버가 여럿으로 나뉘면 이 기억을 어떻게 공유할지가 숙제가 된다. 한 서버에 남긴 기억을 다른 서버가 알지 못하면 신원이 이어지지 않기 때문이다. 이 상태 공유가 이 방식의 대표적인 부담이다.
둘째는 반대로 서버가 상태를 기억하지 않고, 표식 자체에 신원 정보를 담아 스스로 증명하게 하는 방식이다. 서버는 사용자에게 신원과 자격이 담기고 위조를 막는 서명이 붙은 표식을 발급한다. 클라이언트는 요청마다 이 표식을 통째로 내밀고, 서버는 서명을 검증해 그 표식이 자신이 발급한 진짜임을 확인한다. 서버 쪽에 따로 기억해둘 것이 없으니, 서버가 여럿으로 나뉘어도 각자 서명만 검증하면 된다.
이 방식은 서버가 상태를 나눠 가질 필요가 없어 확장에 유리하다. 그러나 표식 안에 신원이 들어 있어 스스로 증명하는 만큼, 한번 발급한 표식을 그 수명이 다하기 전에 도중에 무효로 만들기가 까다롭다. 서버가 상태를 기억하지 않으니, 특정 표식만 콕 집어 죽이려면 별도의 장치를 덧대야 한다. 나는 이 방식을 고를 때 편리한 확장성과 이 무효화의 어려움을 함께 저울에 올린다.
셋째는 사람이 아니라 프로그램 사이의 통신에서 흔한, 발급받은 고정된 열쇠를 그대로 제시하는 방식이다. 어떤 프로그램에 하나의 열쇠를 발급해두고, 그 프로그램은 요청마다 이 열쇠를 실어 자신이 허가된 쪽임을 밝힌다. 사람이 로그인하는 상황보다는 시스템끼리 주고받는 자리에 어울린다. 다만 이 열쇠는 그 자체가 곧 신원이라, 새어 나가면 곧바로 도용되므로 보관과 관리가 특히 중요하다.
이 방식들은 우열로 줄 세울 것이 아니라 쓰임이 다른 도구로 보아야 한다. 서버의 통제가 중요하면 첫째가, 서버를 여럿으로 넓게 펼쳐야 하면 둘째가, 시스템끼리의 통신이면 셋째가 어울린다. 나는 인증 방식을 고를 때 유행이 아니라 이 시스템이 어떤 성질을 더 중히 여기는지를 먼저 따진다. 통제냐 확장이냐, 사람이냐 프로그램이냐 하는 물음의 답이 방식을 부른다.
증명을 담는 자리와 그 규약
어떤 방식을 쓰든 신원 증명을 요청의 어디에 담느냐는 규약이 필요하다. 가장 표준적인 자리가 요청의 Authorization 헤더다. 클라이언트는 이 헤더에 자신의 증명을 담아 보내고, 그 앞에 어떤 종류의 증명인지를 알리는 짧은 표지를 붙인다. 표식을 제시하는 방식이라면 흔히 Bearer라는 표지를 앞세워, 이 표식을 지닌 자에게 자격을 인정한다는 뜻을 담는다.
서버 쪽에서는 인증이 필요한데 증명이 없거나 부실한 요청에 대해, 인증이 필요하다는 신호를 되돌려준다. 앞 편에서 본 실패 코드 가운데 신원이 확인되지 않았음을 뜻하는 코드가 이 신호다. 서버는 이때 어떤 방식의 인증을 기대하는지를 응답 헤더로 함께 알려, 클라이언트가 무엇을 어떻게 내밀어야 할지 짐작하게 한다. 이렇게 요청과 응답이 정해진 규약 위에서 맞물린다.
증명을 담는 자리로 헤더를 쓰는 데는 이유가 있다. 신원 증명은 요청의 본문이나 주소가 아니라, 그 요청에 딸린 부가 정보인 헤더에 담는 것이 자연스럽다. 특히 주소에 증명을 실으면 그 주소가 기록에 남거나 캐시에 저장되며 새어 나갈 위험이 있다. 헤더에 담으면 이런 노출을 줄일 수 있다. 나는 인증 정보를 주소에 노출하는 설계를 위험한 습관으로 보고 피한다.
증명의 자리를 규약으로 못박아두면 클라이언트와 서버가 서로를 이해하기 쉬워진다. 클라이언트는 정해진 헤더에 정해진 표지와 함께 증명을 담고, 서버는 그 자리에서 그것을 꺼내 확인한다. 이 자리와 형식이 흔들리지 않아야, 여러 클라이언트가 같은 방식으로 인증을 붙일 수 있다. 나는 인증의 규약을 성공과 실패 응답의 형태만큼이나 흔들려서는 안 될 약속으로 다룬다.
증명을 다룰 때 놓치기 쉬운 것이 그 증명을 어디에 어떻게 보관하느냐다. 클라이언트가 받은 표식을 함부로 보관하면, 통신은 안전했더라도 저장된 곳에서 새어 나갈 수 있다. 표식은 필요한 만큼만 지니고, 노출되기 쉬운 자리에 방치하지 않아야 한다. 나는 인증을 이야기할 때 발급과 전송뿐 아니라 이 보관까지를 한 흐름으로 본다. 증명은 만들어지는 순간부터 버려지는 순간까지 내내 지켜져야 한다.
정리하면 인증은 무상태 통신에서 요청자의 신원을 세우는 절차이고, 인가와는 다른 질문에 답하며, 첫 확인 뒤 발급한 표식을 정해진 자리에 담아 반복 제시하는 방식으로 이어진다. 서버가 상태를 쥐는 방식과 표식이 스스로 증명하는 방식, 프로그램용 열쇠 방식이 저마다 다른 쓰임을 갖는다. 다음 편에서는 이렇게 만든 서비스가 시간이 지나며 바뀌어야 할 때, 그 변화를 어떻게 관리할지, 곧 버전 관리를 다뤄 보려 한다.