인증이 필요한 이유
인증이란, Front-End 관점에서 봤을 때 로그인, 회원가입과 같은 도입부분을 가리킨다.
반면에 Back-End인 Server-Side 관점에서 봤을 때, 모든 API 요청에 대해 사용자를 확인하는 작업이다.
두 사용자 A와 B가 있다고 가정하자. A와 B는 각자 가지고 있는 정보가 다르다. 따라서 서버에서는 A와 B가 요청을 보냈을 때, 누구의 요청인지 구분 할 줄 알아야 한다. 만일 그렇지 못하다면 타인의 정보가 유출되는 최악의 상황이 발생한다.
그렇기 때문에 Front-End(앱)에서는 각각의 사용자가 본인이 누구인지 알만한 단서를 서버에 보내야한다.
서버에서는 받은 단서를 파악해서 각 요청에 맞는 데이터를 뿌려주게 된다.
서버에 요청을 보내는 작업은 HTTP 메세지를 보내는 것이다. HTTP 메세지의 구조는 다음과 같다.
일반적으로 Header와 Body 이렇게 두가지로 구성되며, 공백은 Header와 Body를 구분하는 역할을 한다.
보통의 모바일이나 웹 서비스의 인증은 HTTP 메세지 속, Header에 인증수단을 넣어 요청을 보내게 됩니다.
아래의 공식문서를 통해 Header에 인증수단을 넣어 요청하는 방법에 대해 확인해보자.
이제 아래의 방식들을 통해 현재 HTTP 기본 인증이 어떻게 이뤄지는지를 알아보자.
가장 보안이 낮은 방식으로 계정정보를 요청(request)에 담아 보내는 방식이다. 위에서 말한 HTTP 요청의 인증할 수단에 비밀번호를 넣는 것이다.
이 방법은 최악의 인증수단으로 데이터를 요청할 때마다 사용자의 비밀스런 정보를 대놓고 보내는 것이다.
보통 Front-End(앱)에서 서버로 HTTP 요청을 할 때 따로 암호화는 되지 않는다.
따라서 해커가 마음만 먹으면 HTTP 요청(request)을 가로채서(intercept) 사용자의 계정정보를 알 수 있다.
본 방식은 절대로 실제 서비스에서 쓰이지 않는다.
(장점)
1. 인증을 테스트 할 때 빠르게 시도해 볼 수 있다.
(단점)
1. 보안에 매우 취약하다.
2. 서버에서는 신호가 올 때마다 ID, PW를 통해 User가 맞는지 인증해야 한다. 이 방법은 비효율적이다.
Session / Cookie 방식
계정정보를 매번 요청(request)에 넣어 보내기엔 보안이 매우 취약하다. 따라서 나온 인증방식이 Session / Cookie 다.
순서를 요약 하자면 아래와 같다.
1. 사용자가 로그인 한다.
2. 서버에서는 계정정보를 읽어 사용자를 확인한다.
3. 사용자의 고유한 ID값을 부여하여 세션 저장소에 저장한 뒤, 이와 연결되는 세션ID를 발행한다.
4. 사용자는 서버에서 해당 세션ID를 받아 쿠키에 저장한 뒤, 인증이 필요한 요청마다 쿠키를 Header에 포함해서 보낸다.
5. 서버에서는 쿠키를 받아 세션 저장소에서 검토를 한 뒤, 대응되는 정보를 가져온다.
6. 인증이 완료되고 서버는 사용자에 맞는 데이터를 보내준다.
세션 쿠키 방식의 인증은 기본적으로 세션 저장소를 필요로 한다. 세션 저장소는 로그인을 했을 때 사용자의 정보를 저장하고 열쇠가 되는 세션ID 값을 만든다.
그리고 사용자는 쿠키로 보관하고 있다가 인증이 필요한 요청(request)에 쿠키(세션ID)를 넣어 보낼 것이다.
웹 서버에서는 세션 저장소애서 쿠키(세션ID)를 받고 저장되어 있는 정보와 매칭시켜 인증을 완료한다.
(장점)
1. 세션/쿠키 방식은 기본적으로 쿠키를 매개로 인증을 거친다. 여기서 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠라고 보면된다. 따라서 쿠키가 담긴 HTTP 요청이 도중에 노출되더라도 쿠키 자체(세션 ID)는 유의미한 값을 갖고있지않다(중요 정보는 서버 세션에) 이는 위의 계정정보를 담아 인증을 거치는 것보단 안전해 보인다.
2. 사용자 A는 1번, 사용자 B는 2번 이런식으로 고유의 ID값을 발급받게 된다. 그렇게 되면 서버에서는 쿠키 값을 받았을 때 일일이 회원정보를 확인할 필요 없이 바로 어떤 회원인지를 확인할 수 있어 서버의 자원에 접근하기 용이할 것이다.
(단점)
1. 장점 1에서 쿠키를 탈취당하더라도 안전할 수 있다고 언급했습니다. 그러나 문제가 하나 있습니다. 만일 A 사용자의 HTTP 요청을 B 사용자(해커)가 가로챘다면 그 안에 들어있는 쿠키도 충분히 훔칠 수 있습니다. 그리고 B 사용자는 그 훔친 쿠키를 이용해 HTTP 요청을 보내면 서버의 세션저장소에서는 A 사용자로 오인해 정보를 잘못 뿌려주게 되겠죠(세션 하이재킹 공격이라고 합니다)
토큰 기반 인증 방식 (ft. JWT)
WT는 세션/쿠키와 함께 모바일과 웹의 인증을 책임지는 대표주자이다. JWT는 Json Web Token의 약자로 인증에 필요한 정보들을 암호화시킨 토큰을 뜻한다. 위의 세션/쿠키 방식과 유사하게 사용자는 Access Token(JWT 토큰)을 HTTP 헤더에 실어 서버로 보내게 된다.
<jwt.io 사이트 캡쳐>
인증순서 전에 간단하게 JWT에 대해 알아보도록 하자. https://jwt.io 를 들어가보면 암호화된 토큰을 볼 수 있다.
토큰을 만들기 위해서는 크게 3가지, Header,Payload, Verify Signature가 필요하다.
Header : 위 3가지 정보를 암호화할 방식(alg), 타입(type) 등이 들어간다.
Payload : 서버에서 보낼 데이터가 들어갑니다. 일반적으로 유저의 고유 ID값, 유효기간이 들어간다.
Verify Signature : Base64 방식으로 인코딩한 Header,payload 그리고 SECRET KEY를 더한 후 서명된다.
최종적인 결과 : Encoded Header + "." + Encoded Payload + "." + Verify Signature
Header, Payload는 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않는다. 따라서 JWT 토큰에서 Header, Payload는 누구나 디코딩하여 확인할 수 있다. 여기서 누구나 디코딩할 수 있다는 말은 Payload에는 유저의 중요한 정보(비밀번호)가 들어가면 쉽게 노출될 수 있다는 말이 된다.
하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없다.
A 사용자가 토큰을 조작하여 B 사용자의 데이터를 훔쳐보고 싶다고 가정해보자. 그래서 payload에 있던 A의 ID를 B의 ID로 바꿔서 다시 인코딩한 후 토큰을 서버로 보냈다. 그러면 서버는 처음에 암호화된 Verify Signature를 검사하게 된다. 여기서 Payload는 B사용자의 정보가 들어가 있으나 Verify Signature는 A의 Payload를 기반으로 암호화되었기 때문에 유효하지 않는 토큰으로 간주하게 된다. 여기서 A사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 걸 확인할 수 있다.
이제부터 JWT가 어떻게 인증에 사용되는지 알아보도록 하자.
1. 사용자가 로그인을 한다.
2. 서버에서는 계정정보를 읽어 사용자를 확인 후, 사용자의 고유한 ID값을 부여한 후, 기타 정보와 함께 Payload에 넣는다.
3. JWT 토큰의 유효기간을 설정한다.
4. 암호화할 SECRET KEY를 이용해 ACCESS TOKEN을 발급한다.
5. 사용자는 Access Token을 받아 저장한 후, 인증이 필요한 요청마다 토큰을 헤더에 실어 보낸다.
6. 서버에서는 해당 토큰의 Verify Signature를 SECRET KEY로 복호화한 후, 조작 여부, 유효기간을 확인한다.
7. 검증이 완료된다면, Payload를 디코딩하여 사용자의 ID에 맞는 데이터를 가져온다.
세션/쿠키 방식과 가장 큰 차이점은 세션/쿠키는 세션 저장소에 유저의 정보를 넣는 반면, JWT는 토큰 안에 유저의 정보들이 넣는다는 점이다. 물론 클라이언트 입장에서는 HTTP 헤더에 세션ID나 토큰을 실어서 보내준다는 점에서는 동일하나, 서버 측에서는 인증을 위해 암호화를 하냐, 별도의 저장소를 이용하냐는 차이가 발생한다.
(장점)
간편하다. 세션/쿠키는 별도의 저장소의 관리가 필요하다. 그러나 JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요없다. 이는 Stateless 한 서버를 만드는 입장에서는 큰 강점이다. 여기서 Stateless는 어떠한 별도의 저장소도 사용하지 않는, 즉 상태를 저장하지 않는 것을 의미한다. 이는 서버를 확장하거나 유지,보수하는데 유리하다.
확장성이 뛰어나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능하다. 예를 들어 Facebook 로그인, Google 로그인 등은 모두 토큰을 기반으로 인증을 한다. 이에 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다.
여기까지의 글만 봤을 때는 JWT가 세션/쿠키 방식보다 더 효율적으로 보인다. 하지만 JWT도 단점들이 존재한다.
(단점)
Payload 정보가 제한적이다. 위에서 언급했다시피 Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. (세션/쿠키 방식에서는 유저의 정보가 전부 서버의 저장소에 안전하게 보관된다) 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없다.
JWT의 길이다. 세션/쿠키 방식에 비해 JWT의 길이는 길다. 따라서 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생하게 된다.