여러 프로젝트를 진행하면서 로그인 기능을 구현해야 하는 상황들을 마주했다. 가장 기본적인 기능이면서 보안적으로 중요한 로그인 기능. 아직까지도 제대로 알지 못하고 사용하고 있어 이번 기회에 개념 정리를 해보았다.
먼저 두 개념의 차이는 다음과 같다:
즉 웹에서 사용자가 웹사이트에 로그인 하는 과정을 인증, 로그인 한 사용자가 특정 리소스에 대한 접근 권한을 확인하는 과정을 인가 라고 할 수 있다.
그러면 어느 웹사이트에서 사용자가 로그인을 진행한다고 생각해보자. HTTP의 무상태성으로 인해 어떤 사용자가 인증을 받더라도 웹서버는 해당 사용자의 다음 요청에서 인증된 사용자라는 것을 알지 못한다. 즉, 매 요청마다 다시 인증을 해야 하는 문제가 발생한다.
이를 해결하기 위한 방법으로는 간단하게 브라우저에 유저 정보를 보관해 뒀다가 매 요청마다 해당 정보를 함께 전달하는 방법이 있다. 하지만 이는 민감한 유저정보를 모두 브라우저에 저장하는 보안적으로 아주 취약한 상태에 있다. 이러한 문제점을 해결하기 위한 방법에 세션과 토큰이 있다.
세션 기반 인가는 사용자의 인증 정보가 서버의 세션 저장소에 저장되는 방식이다.
사용자가 로그인을 하고 유효한 사용자임이 인증이 되면 서버 세션 저장소에 인증 정보를 저장하게 되는데 이때 인증 정보는 Session ID
와 사용자 아이디로 이루어져 있다. 이후 서버에서 Session ID
를 담은 쿠키와 함께 응답을 보내고 브라우저 쿠키에 저장되게 된다. 이후 브라우저에 요청을 보낼 때 마다 헤더에 Session ID
담은 쿠키가 함께 전송되어 서버에서 전달받은 Session ID
와 세션 저장소를 비교해 유효한 인증 정보인지 확인하는 방식이다.
토근 기반 인가는 인증 정보를 토큰의 형태로 클라이언트에 저장하고 있는 방식이다.
그중 가장 대표적인 JWT 토큰 방식에 대해서 알아보자.
Json Web Token 은 세션방식과 큰 차이는 없이 인증에 필요한 모든 정보를 토큰에 담아 암호화 시킨뒤 사용한다. 여기서 가장 큰 차이는 JWT 는 해당 토큰을 서명해서 사용한다는 점이다. 즉, 개인/공개 키로 토큰에 서명을 한 경우 비밀키를 가진 서버에서 해당 토큰이 유효한지를 알수 있다는 말이다.
JWT 는 Header
, Paload
, Signiture
세가지 구성요소로 이루어져 있다. 이 세가지 구성요소는 .
을 기준으로 나누어 구분된다.
헤더는 토큰 타입 그리고 해싱 알고리즘에 대한 정보를 담고 있다. 해싱은 Signiture 부분에서 사용되며 해싱 알고리즘으로는 일반 적으로 HMAC SHA256
혹은 RSA
가 사용된다.
{
"typ": "JWT",
"alg": "HS256"
}
페이로드에는 토큰에 담을 정보가 담겨 있다. 여기에 담는 정보의 한 조각을 클레임(claim) 이라 부르고 키-값 형태를 가진다. 클레임에는 또 세가지 종류가 있다.
iss
: 발급자sub
: 제목aud
: 대상자exp
: 만료시간nbf
: 이 날짜가 지나기 전까지는 토큰이 처리되지 않음iat
: 토큰이 발급된 시간jti
: JWT의 고유 식별자"https://gongibab.com/jwt_claims/is_user": true
"username": "gongibab"
따라서 해당 클레임들을 포함한 예제 페이로드는 다음과 같다:
{
"iss": "gongibab.com",
"exp": "1485270000000",
"https://gongibab.com/jwt_claims/is_user": true,
"userId": "11028373727102",
"username": "gongibab"
}
서명은 헤더의 인코딩 값 그리고 페이로드의 인코딩 값을 합친 뒤 주어진 비밀키로 암호화하여 해쉬를 생성한다. 이 과정을 수도 코드로 나타내면 다음과 같다:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
위 과정들에서 만들어진 값들을 각각 base64
로 인코딩하여 .
을 중간자로 합쳐주면 JWT 토큰이 완성된다.
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ2ZWxvcGVydC5jb20iLCJleHAiOiIxNDg1MjcwMDAwMDAwIiwiaHR0cHM6Ly92ZWxvcGVydC5jb20vand0X2NsYWltcy9pc19hZG1pbiI6dHJ1ZSwidXNlcklkIjoiMTEwMjgzNzM3MjcxMDIiLCJ1c2VybmFtZSI6InZlbG9wZXJ0In0.WE5fMufM0NDSVGJ8cAolXGkyB5RmYwCto1pQwDIqo2w
세션 | 토큰 | |
---|---|---|
사이즈 | 쿠키 헤더에 세션 아이디만 담아 보내면 됨으로 적은 트래픽을 소모함 | 헤더, 페이로드, 서명등 세션 아이디에 비해 훨씬 많은 정보를 담고 있다. 따라서 훨씬 더 많은 트래픽을 소모함 |
안정성과 보안 | 모든 인증 정보를 서버에서 관리하기에 조금더 보안 측면에서 유리하다. 세션 아이디가 탈취된다고 해도 서버에서 해당 세션을 삭제하면 된다. 인증 정보가 서버 세션에 모두 저장되어 있어서 아무 데이터나 저장 가능하다. | 서버에서 인증정보를 관리하지 않기 때문에 토큰이 만료되기 전까지 속수무책이다.페이로드가 암호화 되있지 않아서 누구나 내용을 볼 수 있다. 따라서 민감한 데이터는 토큰에 저장할 수 없다. |
서버 부담 및 확장성 | 사용자가 많아 질 수록 세션 데이터 양이 늘어나면 서버 부담도 비례해 커진다.일반적으로 웹 서버 확장은 수평적으로 일어나는데 이때 세션 기반 인증은 세션 불일치 문제를 겪게 된다. 따라서 세션 스토리지 외부 분리 등의 작업이 필요해진다. | 클라이언트에 인증정보를 저장되어 있으므로 세션 방식에 비해 서버 부담 측면에서 조금 더 유리하다. 위와 같은 이유로 HTTP 의 비상태성을 그대로 활용하여 높은 확장성을 가질 수 있다. |
위에서 언급한 대로 JWT 토큰은 한번 발급한 토큰에 대해서는 제어권이 없고 탈취돼더라도 토큰 만료이전까지 어떻게 할 방법이 없다.
그렇다고 유효기간을 아주 짧게 하자니 사용자가 지속적으로 로그아웃이 되며 사용자 경험에 악영향을 미칠 수 밖에 없다. 이런 문제들을 해결하기 위해 도입된 것이 refresh token
이다.
Access Token
의 유효 기간을 짧게 하여 보안을 강화하면서도 사용자에게 잦은 로그아웃 경험을 주지 않도록 하는 것이다. Access Token
은 리소스에 접근하기 위해서 사용하되, Refresh Token
은 기존에 클라이언트에 저장되 있던 Access Token
이 만료되었을 때 재발급 받기 위해 사용된다.
Refresh Token
은 서버 데이터베이스에 사용자와 매핑되어 저장되어 인증에 사용된다.
Refresh Token
의 매커니즘은 다음과 같다:
Access Token
와 Refresh Token
을 담아 응답한다.Access Token
와 Refresh Token
을 실어 보낸다.Access Token
이 만료 되면, Refresh Token
을 사용하여 새로운 Access Token
을 발급 받는다.
출처 : https://www.rfc-editor.org/rfc/rfc6749.html
인증과 인가 (권한 부여) 비교 – 특징 및 차이점 | Okta Identity Korea
[10분 테코톡] 🎡토니의 인증과 인가
[JWT] 토큰(Token) 기반 인증에 대한 소개
[JWT] JSON Web Token 소개 및 구조
세션 기반 인증과 토큰 기반 인증 (feat. 인증과 인가)
Access Token의 문제점과 Refresh Token
What Are Refresh Tokens and How to Use Them Securely