로그인, 인증 기능 구축 프로젝트[미니 프로젝트 1] - 01. jwt를 이용한 인증 앱 생성하기 : 04. RefreshToken에 대해서

2025. 2. 13. 18:09Programming Language/Node.js

반응형

1. Access Token의 단점

탈취 시 즉시 API 접근 가능 (보안 취약)

Access Token이 탈취되면, 공격자가 토큰을 사용하여 API 요청을 수행할 수 있다.

토큰에는 인증 정보가 포함되어 있어, 탈취되면 제3자가 사용자의 권한으로 API를 호출이 가능하게 되고 이렇게 보호되지 않은 사이트의 경우는 CSRF(크로스 사이트 요청 위조), XSS(크로스 사이트 스크립팅) 등의 공격에 취약하게 된다.

 

만료되면 다시 로그인해야 함

Access Token은 일반적으로 수명이 짧기에(15분 ~ 1시간) 만료되면 사용자는 새 Access Token을 얻기 위해 다시 로그인해야만 한다.

 

Access Token은 자체적으로 취소(Revoke)할 수 없음

Access Token은 JWT 기반이므로 한 번 발급되면 변경할 수 없기에 특정 사용자의 Access Token을 무효화하려면 별도의 블랙리스트 관리가 필요하게 된다.

즉 서버에서는 Access Token이 탈취되었을 경우 즉시 취소할 방법이 없다는 의미이다.

 

이런 단점을을 해결해줄 수 있는 것이 Refresh Token이다.

 

2. Refresh Token이란?

Refresh Token(리프레시 토큰)은 JWT 기반 인증에서 Access Token이 만료되었을 때, 새로운 Access Token을 발급받기 위해 사용되는 토큰으로 보안을 강화하기 위해 사용되며, 일반적으로 서버에서만 저장하고 관리해야 한다.

 

2-1. Refresh Token과 Access Token의 차이점

2-1-1)  Access Token

클라이언트가 서버에 API 요청을 할 때, 인증된 사용자임을 증명하는 역할을 하는 토큰으로 JWT(JSON Web Token)로 만들어지고, 이 안에 사용자 정보와 권한이 포함된다.

만료 시간이 짧은 이유는 유출됐을 때 위험을 줄이기 위함이고 만료되면 클라이언트는 API 요청이 거부된다(401 Unauthorized).

 

2-1-2) Refresh Token

Access Token이 만료됐을 때, 다시 로그인하지 않고 새로운 Access Token을 발급받기 위해 사용한다.

보통 수명이 길고, 서버에 저장하는 게 보안적으로 안전하다.

재사용 감지블랙리스트 처리 등 추가 보안 절차를 두는 게 좋으며 만약 유출된다면, 장기간 공격에 악용될 수 있어서 보안에 Access Token보다 유의해야 한다.

 

두 토큰은 같이 사용되며 보통 사용자가 로그인 하여 Access Token과 Refresh Token을 발급한다

Access Token이 만료 될 때 까지 Access Token으로 API를 요청하고 Access Token이 만료 되면 Refresh Toekn을 서버에 전송하여 Access Token를 새로 발급 받는다.

만약 Refresh Token까지 만료된다면 다시 로그인 해야한다.

 

3. Refresh Token 생성하기

Refresh Token은 위에서 말했다 싶이 Access Token을 생성하면서 같이 생성해서 전달한다.

우리의 코드에서는 아래 login을 받아주는 경로에서 jwt를 사용해서 access token을 생성해주고 있기에 여기에 같이 Refresh token을 생성해서 전달해줄것이다.

그리고 기존엔 Access Token에 유효기간을 전달하지 않았으나 이번에는 유효기간을 설정해서 토큰이 만료 될 수 있도록 설정해 주자.

물론 refresh token의 경우는 이보다 길게 유효기간을 설정해줄것이다.

그리고 refresh token은 쿠키에 저장하지만 httpOnly 옵션(브라우저의 자바스크립트가 쿠키에 접근할 수 없도록 하는 설정)을 설정해서 javascript를 이용해서 탈취하거나 조작할 수 없도록 만들어 줘야만 한다(XSS Cross Site Scripting 공격)

 

잠깐 쿠키를 확인해보면 네이버에 들어가서 개발자 도구를 열어서 Application으로 가보면 

이렇게 쿠기에 대한 정보를 확인할 수 있다.

여기서 Console탭을 선택하고 document.cookie를 입력하면

이렇게 쿠키에 대한 정보를 받아올 수 있게 된다.

보면 HttpOnly가 설정된 쿠키를 뺀 나머지는 모두 가져온 것을 볼 수 있다.

만약 이 HttpOnly 설정을 넣어주면 

이렇게 쿠키를 가져올 수 없게 된다.

 

이렇게 쿠키에 refresh token을 저장하면 서버에서는 이 refresh token을 확인해야하기에 DB에 저장해줘야 한다.

그러나 우리는 아직 DB를 사용하지 않으니 그냥 일단 배열을 생성해서 거기에 담아주기만 하자.

 

3-1. Access Token에 유효기간 설정해주기

이렇게 생성했던 토큰을 accessToken으로 명칭을 변경하고 세번째 인자에 expiresIn이라는 key값으로 유효기간을 추가해주자.

이제 유효기간이 30초인 accessToken이 생성되었다.

Access Token을 생성했으니 이제 Refresh Token을 생성해주도록 하자.

 

3-2. Refresh Token 생성해주기

Refresh Token의 생성은 Access Token의 생성방법과 다르지 않다.

동일하게 jwt 모듈의 sign메서드를 사용하고 첫번째 인자로 payload를 전달해줘야 한다.

우리는 유저의 로그인에 대한 정보를 담을 것이기에 어떤 유저에게 발생한 토큰인지에 대해서 전달해보자.

 

두번째 인자로 이 서명에 사용할 비밀 키를 설정해준다.

이전에 "secret"이라는 문자열을 비밀 키로 설정했기 때문에 이번엔 "superSecret"이라는 문자열을 비밀키로 사용해보자.

 

여기서 비밀 키에 대해서 의문이 생겨 그 부분은 바로 아래 접힌 글 내부에서 설명해두겠다.

더보기

토큰에서 두번째 인자로 사용할 비밀키의 활용 이유

 

내가 의문 이였던 점은 토큰을 생성해서 가져오는건 그렇다고 쳐도 서버에서만 갖고 있는 이 비밀키의 의미가 무엇일지에 대한 의문이였다.

단순하게 그냥 내가 발행한게 맞는지를 확인하기 위함인가 라고 생각 했었는데 그게 끝은 아니고 그 이상의 내용을 추가적으로 설명해보고자 한다.

 

우선 jwt에서 토큰은 세가지 부분으로 나뉜다.

 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NDI2MjM4OTgsImV4cCI6MTc0MjYyMzkyOH0.lE_df9UseEgykL6K6nBWfzfriTlbJb_2HzJtSZSdB5s

 

위는 토큰의 예시인데 여기서 빨간색으로 표시되는 부분은 Header로 해당 토큰에 대한 메타 정보(어떤 알고리즘을 사용하는지, 어떤 타입의 토큰인지)에 대한 정보를 포함하고 있다.

두번째 파란색으로 표시되는 부분은 Payload 부분으로 토큰에 내용물에 해당 하는 부분이다.

이 안에는 토큰의 발행자에 의해 전달되는 내용을 담고 있다.

그리고 이제 주요의 세번째 보라색 부분인데 이건 Signature (서명, 암호토큰) 으로 위조 여부를 확인할 수 있는 부분이다.

 

이 부분은 어떤 알고리즘을 사용해서(디폴트 알고리즘은 HS256 ( HMAC + SHA-256 조합으로 이루어진 서명 알고리즘)) 암호화를 할것이고, 알고리즘의 대상은 Header + Payload로 이 값에 위에 비밀키(문자열 혹은 버퍼(네트워크의 개념에서 버퍼는 데이터를 전달하는 수레와 같지만 소스코드에서 버퍼는 바이너리 데이터를 담기 위한 변수의 타입의 개념으로 봐야한다)로 이루어진)를 사용해 HMAC 해시 값을 생성해내고 이게 바로 저 보라색 부분의 값이 되는 것이다.

 

여기서 비밀키라고 칭하며 사용되는게 우리가 "secret" 혹은 "superSecret"이라는 문자열인 것이다.

그리고 세번째 매개변수로 이 refreshToken의 만료시간을 하루로 설정해주자.

 

그리고 원래는 생성한 시점에 DB에 넣어 저장해줘야 하는데 현재는 DB를 연결하지 않았기 때문에 그냥 배열 하나를 전역변수로 선언해주고

이안에 생성한 refresh token의 값을 넣어줄 수 있도록 해주자.

그리고 accessToken의 경우는 응답에 담아서 전달하나 

refreshToken은 쿠키에 담아 보내줘야 했었으니 토큰에 담아 보내보자.

쿠키에 담기 위해서는 응답의 cookie메서드를 사용한다

더보기

#res의 cookie함수에 대해서

 

* cookie의 함수 원형

res.cookie(name, value, [options]);

 

* cookie의 매개변수

- name(String) - 필수 : 쿠키의 이름으로 클라이언트에서 쿠키를 식별할 때 사용함

- value(String or Object) - 필수 : 쿠키에 저장할 값으로 문자열이 기본이지만 객체도 전달 가능(자동으로 JSON 문자열화됨).

- option(Object) - 선택 : 쿠키의 속성을 정의하는 객체로 만료 시간, 도메인, 보안 옵션 등을 설정함

첫번째 매개변수로 refreshToken이라고 전달해서 구분할 수 있도록 해주고 

두번째 매개변수로 실제 refreshToken 값을 넣어주자.

그리고 이 refreshToken을 js로 탈취할 수 없도록 옵션에 httpOnly를 넣어주자.

추가로 옵션에 토큰의 maxAge는 길게 설정해주자. 

그리고 이제 정상적으로 작동하는지 확인해보자.

 

3-3. refreshToken 정상 발행 확인

postman을 열어주고 아래와 같이 설정한 후에 요청을 던져보면

응답으로 accessToken을 전달하고

Cookie 탭에서 refreshToken을 전달하는 것을

볼 수 있다.

 

여기서 accessToken을 유효기간을 30s로 해뒀기에 조금만 지나도 해당 토큰을 사용해서 아래 url,

위 소스에 접근 할 수 없어진다.

 

확인해보면 아래와 같이 접근이 거부된 것을 볼 수 있다.

 

이제 다음엔 위에서 생성했던 refreshToken을 사용해서 accessToken을 생성해보도록 하자.

 

반응형