인증과 인가, Session과 JWT

💛 nalsae·2023년 5월 11일
1

🤍 내보정 CS

목록 보기
1/1
post-thumbnail

 프로젝트를 진행하다 보면 자연스레 로그인 및 회원가입 기능 구현이 포함되는 경우가 많다. 로그인과 회원가입 기능은 프론트, 즉 클라이언트 단에서 서버 단으로 Requset를 보내고 Response를 받아야 하는 과정이 필연적이다 보니, 처음 접했을 때는 서버 로직을 한 번도 구현해보지 않았던 나에게 너무 난감하게 느껴졌다. 그래서 기능 구현 전에 어떤 방식으로 로그인 기능을 구현할 수 있는지 기술 검토를 철저하게 했고, 이번 글에서는 그 내용을 소개해보고자 한다.

🔑 인증? 인가?

 먼저 로그인 기능에 필요한 기술 스택을 사용하기 전에 선행적으로 이해해야 하는 개념이 있다. 바로 인증과 인가이다. 얼핏 보면 유사한 개념처럼 보이지만, 자세히 들여다 보면 큰 차이가 있는 개념들이다.

인증이란?

 인증(Authentication)은 사용자가 웹 사이트에 최초로 로그인할 때 적용되는 개념이다. 클라이언트는 사용자가 입력한 아이디와 패스워드를 서버로 Request 보내고, 서버 측에는 전달 받은 사용자 정보를 DB와 비교하여 DB에 등록된 사용자일 경우 로그인을 허용한다. 이처럼 웹 사이트 최초 접속 시 등록된 사용자 정보인지 서버에 확인하는 것을 인증이라고 한다.

인가란?

 인가(Authorization)은 사용자의 최초 인증이 완료된 후 페이지를 이동할 때마다 로그인을 해야 하는 번거로움을 없애고 싶을 때 적용할 수 있는 개념이다. 즉, 사용자가 로그인했다는 인증 정보를 저장하여 로그인을 유지한 상태로 페이지를 이동할 수 있게끔 하는 것이다. 그리고 이러한 인가의 방법으로 Session과 JWT가 있다. 최근의 간편 로그인 기능에는 0Auth 2.0 방식을 많이 사용하긴 하지만, 이번 게시글에서는 Session과 JWT에 대해서만 다루고자 한다.


❓ Session? JWT?

🔸 Session이란?

 Session을 쉽게 설명하면 웹 서버와 연결된 브라우저가 종료될 때까지의 시점을 의미한다. 그렇다면 세션을 통해 인가를 어떻게 구현할 수 있을까?

 순차적으로 살펴보면 다음과 같다.

  1. 사용자가 최초로 로그인 정보를 넘기면 서버가 로그인 정보를 바탕으로 Session ID를 생성하여 Session DB에 저장한다.
  2. HTTP 응답을 보낼 때 생성한 Session ID를 쿠키에 함께 동봉하여 보낸다.
  3. 클라이언트에서는 요청을 할 때마다 쿠키에 저장된 Session ID를 자동으로 HTTP 요청 헤더의 쿠키에 담아서 전송한다.
  4. 서버에서 이를 Session DB에 저장된 사용자 정보와 대조하고, 일치한다면 인가를 정상적으로 수행한다.

 일련의 과정 중에 쿠키에 유효기간, httponly, secure 등의 부가적인 옵션을 함께 저장함으로써 외부 보안 공격의 위험을 일정 부분은 해소할 수 있다. Session을 사용한 인가의 장단점은 다음과 같다.

👍 Session 사용의 장점

  • Session DB에 로그인한 사용자 정보를 저장하므로, 저장한 정보를 바탕으로 새로운 기능 추가가 가능하다.
    ex) 강제 로그아웃, 한 계정에 로그인할 수 있는 유저 수 지정 등
  • 서버에서 관리하기 때문에 상대적으로 온전한 상태를 유지할 수 있다.
  • 토큰에 비해서 Session ID의 크기가 매우 작다.

👎 Session 사용의 단점

  • Session DB에 Session ID로 클라이언트의 상태를 저장하기 때문에 HTTP 요청의 특징인 Stateless를 위반한다.
  • Session DB, 즉 서버에 저장되기 때문에 사용자 수가 많아져서 과부하가 걸릴 수 있다.

 이처럼 Session을 사용한다는 건 곧 서버의 부담이 커진다는 것을 의미한다. 그렇다면 서버 과부하를 해소하기 위해 서버를 여러 대 두면 이러한 단점을 해결할 수 있지 않을까? 그렇지는 않다. Session을 사용할 때 서버를 여러 대 두면 사용이 굉장히 복잡해진다. HTTP 요청은 기본적으로 Stateless, 즉 상태를 기억하지 않기 때문에 다른 서버로 이동할 때마다 결국 로그인을 다시 해야 한다는 문제가 발생하기 때문이다.


🔸 JWT란?

 JWT는 JSON Web Token의 줄임말로, 앞서 살펴본 Session의 단점을 해결하기 위해 사용되는 인가 방식이자 하나의 긴 문자열이다. JWT는 header, payload, signature의 3단 구조로 구성되어 있는데, 구체적으로 살펴보면 다음과 같다.

// header
{
 "alg": "HS256",
 "typ": "JWT"
}

 먼저 header에는 토큰의 타입과 해싱 알고리즘에 대한 내용이 저장된다.

// payload
{
 "iss": "jh.com", // 등록된 클레임
 "exp": "1485270000000", // 등록된 클레임
 "<https://xxx.com/jwt_claims/is_admin>": true, // 공개 클레임
 "userId": "11028373727102", // 비공개 클레임
 "username": "jh" // 비공개 클레임
}

 payload는 토큰에 담을 정보들이 존재한다. 여기서 키: 값의 형태로 존재하는 정보를 클레임이라고 부른다. 클레임은 다시 그 성격에 따라 등록된 클레임, 공개 클레임, 비공개 클레임으로 구분하기도 한다.

 마지막으로 signature에는 토큰을 인코딩하거나, 서버에서 토큰의 유효성 검증을 진행할 때 사용되는 암호화 코드가 담긴다.

// JWT
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

 3단 구조가 결합된 JWT의 모습은 위와 같다. 구체적으로 header와 payload 값을 각각 BASE64 방식으로 인코딩하고, 암호 키를 이용하여 header에 정의해놓은 알고리즘으로 해싱한다. 그 후 한 번 더 BASE64 방식으로 인코딩한다.

 그렇다면 지금까지 살펴본 JWT를 사용하여 구체적으로 인가를 어떻게 구현할 수 있을까?

 순차적으로 살펴보면 다음과 같다.

  1. 클라이언트에서의 사용자의 최초 로그인 정보를 넘기면 서버는 그 정보를 토대로 토큰을 생성한다.
  2. 생성한 토큰은 서버에 따로 저장하지 않고 HTTP 응답을 보낼 때 쿠키에 동봉하여 전송한다.
  3. 클라이언트에서 토큰을 받아서 요청을 보낼 때마다 HTTP 요청 헤더에 담아서 전송한다.
  4. 서버는 헤더의 토큰 안에 있는 signature를 토대로 유효성 검사를 진행하고 유효성 검사 결과, 즉 인가 결과를 클라이언트에 전달한다.

 추가적으로 발급한 토큰의 유효 기간을 짧게 설정하고, accessTokenrefreshToken을 이용하여 accessToken의 유효 기간이 종료될 때마다 refreshToken을 사용하여 accessToken을 새로 발급받는 방식으로 인가를 진행하면 보안을 좀 더 보완할 수 있다. JWT를 사용한 인가의 장단점은 다음과 같다.

👍 JWT 사용의 장점

  • 서버에 따로 토큰을 저장하지 않기 때문에 과부하를 신경쓰지 않아도 된다.
  • header와 payload 값을 바탕으로 signature를 생성하기 때문에 데이터 위변조를 막을 수 있다.
  • JWT 토큰이 자체적으로 필요한 정보를 가지고 있다.

👎 JWT 사용의 단점

  • Session ID보다 토큰의 크기가 훨씬 크기 때문에 인증 요청이 많아질수록 네트워크 부하가 발생할 수 있다.
  • payload 자체는 암호화되지 않기 때문에 토큰에 보안이 필요한 정보를 담을 수 없다.
  • 토큰을 탈취 당하면 토큰이 만료되기 전까지 대처할 수 없다.

🤔 그럼 언제 Session 쓰고, 언제 JWT 쓰지?

 물론 상황에 따라 합리적인 선택을 하는 것이 제일 바람직하기 때문에 딱 떨어지는 정답은 없다. 그래도 각각의 특징을 토대로 선택 기준을 나름대로 정의해보자면 다음과 같이 정의할 수 있을 것 같다. 규모가 작은 서비스는 JWT를 사용하여 간단하게 인가를 구현하되, 서비스 규모가 확장되어 서버 규모가 커지고 사용자 정보를 토대로 기능 확장이 필요하다면 Session을 사용하여 인가를 구현하는 것이 바람직할 것이다.

💡 참고 문헌

: https://fierycoding.tistory.com/69#rp

profile
𝙸'𝚖 𝚊 𝚍𝚎𝚟𝚎𝚕𝚘𝚙𝚎𝚛 𝚝𝚛𝚢𝚒𝚗𝚐 𝚝𝚘 𝚜𝚝𝚞𝚍𝚢 𝚊𝚕𝚠𝚊𝚢𝚜. 🤔

0개의 댓글