인증(Authentication) vs 인가/권한부여(Authorization)
인증과 인가/권한부여 인증 방식에 대해서 알기 위해서 필요한 사전 지식으로 두 단어의 정확한 의미를 알고 서로의 의미를 혼돈해서는 안됩니다.
인증과 인가를 출입증에 비교해서 설명하는 예를 들어보겠습니다.
인증(Authentication)이란, 방문자가 회사 건물에 출입하려고 할때 확인 받는 과정입니다. 이 과정을 통해서 자신의 신상을 확인할 수 있으며 출입 여부를 확인 받을 수 있습니다.
인가/권한부여(Authorization)이란, 방문자가 회사 건물에 방문했을 때, 허가된 공간에만 접근 가능하며, 개인마다 회사 건물에서 출입할 수 있는 공간의 차이가 존재할 수 있습니다.(vip, 일반 사원, 외부 방문자)
HTTP 통신은 현재 WEB 통신 수단으로 가장 많이 사용되는 방식입니다. 그런 HTTP는 비연결성 및 무상태성이라는 특징을 가지고 있습니다. 이런 특징으로 인해서 HTTP 통신은 응답 후 연결을 끊고 과거에 요청한 정보를 기억하지 않습니다. HTTP의 특징으로 인해서 앱을 이용하는 사용자가 여러명이 있을 경우 A 사용자와 B 사용자의 정보 및 콘텐츠가 달라야 하지만 HTTP는 자신의 특징으로 인해서 사용자를 구분할 수 없는 현상이 발생하였습니다. 이러한 문제로 인증을 사용하게 되었습니다.
즉, HTTP는 비연결성 및 무상태의 특징을 가지고 있어서 요청을 보낸 직후 연결을 끊고 정보를 저장하지 않아서 인증이라는 절차를 통해서 확인할 수 있는 수단이 필요합니다.
▪︎ 비연결성(Connectionless): 클라이언트와 서버가 한 번 연결을 맺을 후, 클라이언트 요청에 대해 서버가 응답을 마치면 맺었던 열결을 끊어버리는 성질을 말합니다.
▪︎ 무상태(Stateless): 이전 요청과 무관한 각각의 요청을 독립적으로 취급하며 상태를 서버에 저장하지 않는 성질입니다.
HTTP 쿠키(Cookie)는 서버가 사용자의 웹 브라우저에 전송하는 작은 데이터 조각으로, 서버를 통해 웹 브라우저에 설치되는 작은 기록 정보입니다. Cookie를 이용한 인증 방식은 가장 쉽게 구현을 할 수 있지만 모든 정보가 public해서 쉽게 정보가 유출 될 수 있는 점을 유의해야 합니다.
Cookie를 이용한 인증 방법은 계정 정보가 public해서 보안에 매우 취약합니다. 보안이 좋지 않다면 인증을 한다고 해도 인증 절차를 신뢰할 수 없습니다. 그래서 보안된 방법으로 Session / Cookie를 이용한 인증 방법이 등장했습니다.
Session / Cookie 인증 방식은 기본적으로 세션 저장소가 필요합니다. 세션 저장소는 로그인 정보를 통해서 사용자의 정보를 저장하고 열쇠가 되는 세션ID값을 만들어 줍니다. 그리고 HTTP 헤더에 세션ID를 담아 사용자에게 보냅니다. 그러면 클라이언트에서는 쿠키로 보관하고 인증을 하는 경우에 요청에 쿠키를 담아 보냅니다. 웹 서버에서는 세션 저장소에서 쿠키(세션ID)를 받고 저장되어 있는 정보와 매칭시켜 인증을 완료합니다.
쿠키를 탈취당하는 것을 방지하기 위해서는 HTTPS를 사용하여 요청을 탈취해도 해석할 수 없게 만드는 방법이 있고, 세션의 유효시간을 넣거나 탈취당하면 세션을 파기시키는 방법이 있습니다.
JWT(JSON Web Token)이란 인증에 필요한 정보들을 암호화를 통해서 토큰을 관리하는 것을 의미합니다. JWT 기반 인증은 Session / Cookie 인증 방식과 같이 앱 인증에서 가장 보편적으로 사용되는 방식입니다. 또한 Session / Cookie 인증 방식과 유사하게 사용되는 Access Token(JWT Token)을 HTTP 헤더에 담아서 서버로 보내는 방식입니다.
Header, Payload는 인코딩될 뿐(16진수로 변경), 따로 암호화되지 않습니다. 따라서 JWT 토큰에서 Header, Payload는 누구나 디코딩하여 확인할 수 있습니다. 여기서 누구나 디코딩할 수 있다는 말은 Payload에는 유저의 중요한 정보(비밀번호)가 들어가면 쉽게 노출될 수 있다는 말이 됩니다. 하지만 Verify Signature는 SECRET KEY를 알지 못하면 복호화할 수 없습니다.
인코딩(Encoding)
: 컴퓨터에서 인코딩은 동영상이나 문자 인코딩 뿐 아니라 사람이 인지할 수 있는 형태의 데이터를 약속된 규칙에 의해 컴퓨터가 사용하는 0과 1로 변환하는 과정을 통틀어 말합니다. (코드화 = 암호화)
디코딩(Decoding)
: 디코딩은 인코딩의 반대로서 사람이 이해 할 수 있도록 바꿔주는 것을 의미합니다. 즉, 바이트 형식을 문자열로 변환한다는 의미입니다.
A 사용자가 토큰을 조작하여 B 사용자의 데이터를 훔쳐보고 싶다고 가정하겠습니다. 그래서 payload에 있던 A의 ID를 B의 ID로 바꿔서 다시 인코딩한 후 토큰을 서버로 보냈습니다. 그러면 서버는 처음에 암호화된 Verify Signature를 검사하게 됩니다. 여기서 Payload는 B사용자의 정보가 들어가 있으나 Verify Signature는 A의 Payload를 기반으로 암호화되었기 때문에 유효하지 않는 토큰으로 간주하게 됩니다. 여기서 A사용자는 SECRET KEY를 알지 못하는 이상 토큰을 조작할 수 없다는 걸 확인할 수 있습니다.
이미 발급된 JWT에 대해서는 돌이킬 수 없습니다. 세션/쿠키의 경우 만일 쿠키가 악의적으로 이용된다면, 해당하는 세션을 지워버리면 됩니다. 하지만 JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능합니다. 따라서 악의적인 사용자는 유효기간이 지나기 전까지 신나게 정보들을 털어갈 수 있습니다.
해결책: 기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급합니다. 그렇게 되면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있습니다. 이는 다음 포스팅에 나올 Oauth2에 더 자세히 다루도록 하겠습니다.
JWT의 단점을 보안하기 위해서 많은 방법이 하나의 방법만을 사용하기 보다 여러가지 대체 방안을 활용하면 보안 안전에 크게 도움이 됩니다.
토큰의 만료 시간을 짧게 설정하는 방법을 고려할 수 있습니다. 토큰이 탈취되더라도 빠르게 만료되기 때문에 피해를 최소화할 수 있습니다. 하지만 탈취된 동안은 대처할 수 없는 단점을 완벽하게 해결할 수는 없으며 자주 토큰을 발급받아야 하는 단점도 존재합니다.
Access Token(JWT)를 통한 인증 방식의 문제는 만일 제 3자에게 탈취당할 경우 보안에 취약하다는 점입니다. 위에서 제시한 만료 기한을 짧게 설정하는 방식이 존재하지만 여러번 로그인을 통한 인증받아야 하는 단점이 있었습니다. Refresh Token을 이용하면 만료 기한을 짧게 설정하고 로그인을 여러번 하는 시도하는 문제를 해결할 수 있습니다.
Refresh Token은 Access Token과 똑같은 형태의 JWT입니다. 처음에 로그인을 완료했을 때 Access Token과 동시에 발급되는 Refresh Token은 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 됩니다.
사용 예를 간단히 들어보겠습니다. Refresh Token의 유효기간은 2주, Access Token의 유효기간은 1시간이라 하겠습니다. 사용자는 API 요청을 신나게 하다가 1시간이 지나게 되면, 가지고 있는 Access Token은 만료됩니다. 그러면 Refresh Token의 유효기간 전까지는 Access Token을 새롭게 발급받을 수 있습니다.
인증 절차
장점
기존에서 사용하던 JWT(Access Token만을 사용한 인증)보다 더욱 안전한 인증 절차입니다.
단점