로그인, 회원가입은 어떠한 작업을 통해 이루어지는것일까?
현대의 웹, 앱, 게임 등 여러 서비스와 페이지를 접할때 해당 서비스를 이용하려면 회원가입과 로그인을 해야 모든 서비스를 이용할수 있도록 하는 서비스들이 대부분입니다.
로그인은 사용자들이 로그인을 할때 불편함이 없도록 해야 하며, 로그인을 하고나면 로그인 상태가 계속 유지될수 있도록 해야하고, 서버에서 사용자가 로그인이 되어 있다는것을 알수 있도록 해야합니다.
그렇다면 이런 회원가입과 로그인에 대한 절차와 어떻게 내가 회원가입을 한 정보가 저장이 되고 나중에도 사용될 수 있는것일까요? 해당 질문에 대한 답변에는 서버가 클라이언트의 인증을 확인하고 인가하여 처리하는 방법이 여러가지가 있기 때문에 로그인에 대한 여러가지 인증 방식에 대해 알아보고,
그중에서도 흔히 사용하는 JWT 방식에 대해 알아보겠습니다!
Authentication vs Authorization
인증(Authentication)과 인가(Authorization)은 비슷한 개념인것 같이 보여 자칫 혼동하며 사용할수 있는 단어들입니다.
이렇게 헷갈릴수 있는 인가와 인증의 차이점을 알아보면,
인증(Authentication)은 쉽게 말해서 사용자가 로그인을 하는 행위로, 사용자가 로그인을 했을때 내가 이 사이트에 가입된 회원임을, 즉 특정 서비스에 일정 권한이 주어진 사용자임을 아이디랑 패스워드 등을 통해서 말 그대로 인증을 받는것입니다.
그에반해 인가(Authorization)는 인증을 받은 사용자가 이후 서비스의 여러 기능들을 사용할 때 사용자가 로그인으로 인증을 하고 나서 사용자가 로그인 한 해당 계정으로만 할수 있는 활동을 시도할수 있도록 사용자에게 사용권한을 부여해주는것입니다.
이렇게 로그인을 할때는 인증방식과 인가방식으로 로그인을 구현할수 있는데, 서버에서 클라이언트에게 인가를 해주는 방식은 예전부터 여러방식이 존재하였습니다. 대표적인 방식으로는 쿠키, 세션, 토큰 등의 방식이 존재하는데 그러면 이 3가지 방식에 대한 차이점과 한계점, 그리고 어떤것을 사용하는게 가장 좋은것인지 알아보겠습니다!
세션과 토큰이 주로 사용되기전에는 쿠키라는 인증방식이 존재.
쿠키 인증방식은 사용자가 직접 들고다니면서 시설을 이용할때마다 사용자의 쿠키를 보여주는것으로, 사이트를 방문하고 이용할때 브라우저에 저장되는 내용들입니다. 하지만 이 쿠키는 사용자가 임의로 고치거나 지울 수 있고 보안에 매우 취약합니다. 때문에 클라이언트가 직접 관리하는것보다 서버에서 직접 보관하고 관리해야 하는 정보들이 존재합니다. 이렇게해서 등장하는 방식인
세션 인증방식은 사용자가 로그인에 성공하게 되면 서버는 '세션 표딱지'라는걸 출력합니다.
(예를 들면 영화관 티켓같이 내가 로그인을 했다는 인증표 형식같은 느낌) 그리고 이것을 두 부분으로 나눠서, 한 부분은 사용자에게 보내고, 나머지 부분은 자신의 메모리에 올려놓게 됩니다.
이러한 세션 아이디가 실려오면 서버는 그것을 메모리에서 맞는 짝이 있는지 찾아가지고 있으면 사용자에게 인가를 허용해주게 됩니다. 이러한 세션 ID를 이용해서 사용자가 서버에 로그인이 되어있음을 지속하게 하는 상태를 세션이라고 합니다! 하지만 역시 이 세션 방식에도 허점은 존재하게 되는데, 사용자가 동시에 많은 접속을 하게 되면 메모리가 부족해지고, 메모리에 에러가 생기게 되면 메모리에 있던 것들은 모두 날라가서 모든 사용자들이 재로그인을 해야하는 상황이 올수도 있고, 서비스의 규모가 커지게 되면 세션 유지의 어려움이 생기게 됩니다.
그렇다면 이러한 세션방식의 단점을 보완한 방식은 무엇일까요? 바로 이제 토큰이 등장합니다.
토큰 인증방식은 사용자가 로그인을 하면 토큰을 건내주게 되고, (이때 서버는 사용자의 정보를 기억하고 있지 않음.) 이러한 토큰은 인코딩 또는 암호화된 3가지 데이터를 이어붙여져 있는데, 이 토큰이 바로 JWT (JSON WEB TOKEN) 토큰입니다.
JWT 토큰에는 사용자가 로그인을 하고 나서 받는 토큰에 사용자의 관한 정보들이 실려오게되고, 이것이 그 이후 요청들마다 이번에는 사용자로부터 서버한테 보내지게 됩니다.
아까 위에서 말했듯이 JWT 토큰 인증방식은 사용자가 로그인을 하면 토큰을 발급하는 형식으로, 이 토큰은 3가지 데이터를 이어붙힌 토큰입니다. 이러한 3가지 데이터는 Header , PayLoad , Signature로 이루어져 있고, 각각을 조금 더 자세히 설명해보면
1. Header (헤더)
헤더는 JWT의 첫 번째 부분으로, 토큰에 대한 메타데이터가 들어있습니다. 주로 두 가지 필드를 포함하는데, 이는 토큰의 형식과 암호화 알고리즘을 나타냅니다.
alg (알고리즘): JWT가 서명을 생성할 때 사용하는 암호화 알고리즘을 지정합니다. 주로 HMAC SHA256(HS256), RSA 또는 ECDSA 같은 알고리즘이 사용됩니다. 기본적으로 HS256이 많이 사용됩니다.
typ (타입): 이 필드는 토큰의 타입을 나타냅니다. 대부분의 경우 JWT 토큰이므로 "JWT"로 설정됩니다.
헤더는 간단한 JSON 객체로 표현되며, 이 정보는 Base64 URL 인코딩이 되어 JWT의 첫 번째 부분을 형성합니다.
헤더의 예시를 표현하면
{
"alg": "HS256",
"typ": "JWT"
}
형식으로 표현하고, 헤더의 의미는 해석할 때 클라이언트와 서버가 동일한 알고리즘을 사용해 서명을 검증할 수 있도록 도와줍니다. HS256은 HMAC(SHA-256) 알고리즘을 사용한다는 의미입니다.
2. Payload (페이로드)
페이로드(Payload)는 JWT의 두 번째 부분이며, 사용자 정보와 같은 클레임(claim)이 포함됩니다. 이 클레임은 JWT에서 여러 종류가 있으며, 각 클레임은 이름과 값으로 구성됩니다.
클레임 종류
Registered 클레임: JWT 표준에서 미리 정의된 클레임입니다. 대표적으로 아래와 같은 클레임들이 있습니다:
iss (issuer): 토큰 발행자
sub (subject): 토큰의 주체, 주로 사용자 ID
aud (audience): 토큰의 대상
exp (expiration time): 토큰의 만료 시간
iat (issued at): 토큰이 발행된 시간
Public 클레임: 등록되지 않은 클레임으로, 사용자가 자유롭게 정의할 수 있지만, 충돌을 방지하기 위해 URI 형식으로 네임스페이스를 제공하는 것이 좋습니다.
Private 클레임: 서버와 클라이언트 간에만 사용되는 클레임입니다. 이 클레임은 공개되지 않으며, 서버가 이해하고 사용할 수 있는 데이터를 담습니다.
페이로드의 예시를 표현하면
{
"userId": 101,
"username": "testUser",
"role": "user",
"exp": 1634163438
}
형식으로 표현하고, 이 페이로드는 사용자 ID, 사용자 이름, 권한(역할), 만료 시간 등을 포함하는 예시입니다.
3. Signature (시그니처)
시그니처(Signature)는 JWT의 마지막 부분으로, 헤더와 페이로드가 변조되지 않았음을 확인하는 역할을 수행합니다. JWT의 서명은
위 3가지 절차를 통하여 생성됩니다.
JWT 토큰의 핵심은 서명인데, 페이로드(내용)과 secret key(비밀번호)을 서명을 하는 알고리즘에 집어넣게 되면 그에 따른 서명을 생성하고, 토큰 형식으로 사용자에게 전달해주고 서버는 이것의 대한 내용을 잊어버리게 됩니다. 하지만 서버는 이렇게 내용을 잊어버리더라도 나중에 이 토큰이 서버 자신이 만든 내용이라는것을 알수 있어야하는데, 이때 페이로드와 secret key를 넣은 서명을 알고리즘에 대입하여 일치하면 이것이 자신이 만든 토큰임을 인지하게 됩니다.
JWT 인증 방식의 장점으로는
이러한 장점들 때문에 현재 JWT 토큰 인증 방식은 로그인을 구현할때 매우 유용하게 사용되고 있습니다.
이렇게 들어보면 JWT 토큰은 매우 효율적이고 장점만 있는 방식이라고 생각이 들수 있지만, 세션의 모든걸 대체하기에는 JWT에도 단점이 존재합니다.
세션은 모든 사용자들의 상태를 기억하고 있기때문에 구현에 부담이 되는것은 사실이지만, 구현이 되면 기억하는 대상의 상태들을 언제든 제어할수 있게 됩니다. 이는 서버가 사용자들을 제어할수 있는 권한을 가질수 있다는 말인데, 이러한 세션 방식과 다르게 JWT 인증 방식은 토큰을 주기만하고 사용자의 정보를 기억하고 있지 않아서 이미 줬던 토큰을 다시 회수할수도 없고 그 토큰의 발급 내역이나 정보를 기억하지 못하기 때문에 서버가 사용자를 통제할수가 없습니다.
때문에 요즘은 토큰을 다른사람에게 탈취당하는 사건에 대비하기 위해 로그인을 하면 수명이 몇 분 이하로 짧은 Access 토큰과 1-2주 정도로 잡혀 있는 Refresh 토큰 두개를 발급해줍니다. 이러한 Refresh 토큰은 상응값을 데이터베이스에도 저장하여 Access 토큰의 수명이 다하면 Refresh 토큰을 보내고, 서버는 그것을 데이터베이스에 저장된 값과 대조해보고 맞다면 새 Access 토큰을 발급해주는 방식을 사용합니다.
🤡 결론
아무리 JWT가 구현하기 편리하고 좋더라도 세션방식과 JWT 토큰 방식은 명확하게 각각의 장단점이 존재하기 때문에 상황에 따라 어떤것을 사용하는게 적합한지 잘 생각해보고 사용하는게 제일 좋다고 생각합니다!