파크로그
article thumbnail
Published 2022. 8. 25. 19:43
[back] credentials (cookie) Project/Co-Studo

TL;DR

백엔드에서는 Cookie 를 Setting 해주기 위해 CORS 설정 'Access-Control-Allow-Credentials': 'true' 을 해줘야 한다.

 

back 서버에서 token 넘겨주기

JwtResponse 를 데이터 값으로 넘겨주는 것(payload) 에서 set cookie 하는 방식으로 변경

- 데이터 값으로 넘겨주는 것을 어떻게 탈취하는 걸까?

- setCookie 로 하기 위해서 express 에서 제공하는 response cookie 메서드를 사용했다.

 

  res.cookie('accessToken', appAccessToken, {
    maxAge: 120_000,
    httpOnly: true,
    secure: true,
  });

이 상태로 보내주니 Network 탭에서는 Set-Cookie 헤더에 token 을 확인할 수 있었는데, Application 탭에서는 Cookie 가 저장되는 것을 볼 수 없었다.

 

관련된 내용을 찾아보니 http request 를 할 때, with credential 이라는 옵션을 통해 서버에서 받은 cookie 를 바로 저장할 수 있도록 요청해야 했다.

 

// Front 에서 요청할 때

// 전
const {
    data: { results: jwtResponse },
  } = await axios.get<{ ok: boolean; results: JwtResponse }>(loginUrl);
      
// 후
const {
    data: { results: jwtResponse },
  } = await axios.get<{ ok: boolean; results: JwtResponse }>(loginUrl, {
    withCredentials: true,
  });

 

Front 에서만 withCredentials 옵션을 주니, 이제는 CORS 관련 에러가 뜬다.

 

Origin 관련한 CORS 설정은 middleware 에서 처리해줬으니, Credentials 관련 된 것도 추가해주면 될 것으로 생각했다. MDN

맨 처음엔 CORS 설정이니 모든 요청에 처리해줘야 하나 생각했지만 Credentials 관련 한 요청에서만 setting 해주는게 맞다고 생각하여 사용하고 있는 곳에서 설정해주었다. 

 

  // 'true' 는 문자열이 아니면 오류가 난다.
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  
  res.cookie('accessToken', appAccessToken, {
    maxAge: 120_000,
    httpOnly: true,
    secure: true,
  });

Application 의 Cookie 탭에서 확인할 수 있었다. HttpOnly 와 Secure 관련 설정도 된 것을 확인할 수 있었다.

이제 쿠키를 받았으니, 정보 요청을 위해 쿠키를 보내기도 해야하는데 쿠키를 보낼 때에도 마찬가지로 withCredentials 옵션을 포함하여 보내야 한다.

 

 const {
    data: {
      results: { email, nickname, avatarUrl },
    },
  } = await axios.get<{ ok: boolean; results: User }>(
    `__API_END_POINT__/api/user/me`,
    {
      withCredentials: true,
    },
  );

token maxAge (브라우저의 expire 시간) jwt expiresIn ( 서버의 jwt token expire 시간 )

서비스 자체적인 token 의 expire 시간도 있지만, 브라우저에서에서의 expire 시간을 설정할 수 있다.

브라우저 시간과 동일하게 가져갈 지, ( 서버에서 access-token 이나 refresh-token 이 없는 경우를 새로 구현해야한다 )

편하게 브라우저에서는 토큰의 유효기간을 길게 가져가고, access-token 이나 refresh-token 의 유효함만 체크할 지,

각각의 장단점을 확인해보고 결정해야겠다.

 

쿠키 만료 시?

어떤 서비스는 로그인 정보가 만료되었다고 로그인으로 유도하는 서비스도 있고, 로그인을 풀어놓기만 하는 서비스도 있다.

전자가 사용자 경험 측면에서 더 편하게 느껴졌는 데, 해당 서비스의 token 을 추적해보니 local storage 에 저장하는 방식이라 브라우저 cookie 의 유효기간을 길게 가져가는 방법과 동일하다고 느꼈다.

 

보안측면에서 이런 token 의 값을 남겨도 괜찮은 지 고민이 되고, 혹은 local storage 에 token 값 그대로 저장하는 것이 아니라 사용자가 로그인을 했던 적이 있는지, 임의로 로그아웃을 한 적이 있는지, 로그아웃을 한 적이 없다면 로그인이 되어 있어야 하는데 cookie 가 존재하지 않는다면 로그인이 만료되었다고 판단하는 식으로 진행해도 괜찮겠다는 생각이 들었다.

 

Refresh Token, Access Token, +) CSRF Token ?

 

현재 백엔드 서버는 Refresh Token 과 Access Token 을 JWT 를 통해 관리하고 있는데,

Cookie 를 사용하면 꼭 같이 오는 해킹 기법(CSRF) 가 오는 듯 하다.

 

국내 블로그글 중에서 참고한 글에서는 Access Token 을 앱 내부의 지역변수로 받아 사용하여 방지하는 것을 이야기하는데, 해당 부분관련하여 이야기할 기회가 있었다. 그런데 그때 앱 내부에 있는 토큰이 더 탈취하기 쉽다라고 말해주신 부분도 있어서(?) 이 토큰 값들을 어떻게 관리하는게 좋을지, CSRF 가 문제라면 CSRF 를 방지할 방법이 무엇인지에 대해 좀 더 알아보았던 것 같다.

 

1. CSRF token 사용

2. Cookie 의 SameSite attribute 사용

 

1 번은 CSRF token 을 위한 로직을 추가해야하는데, 관련하여 아는 지식이 없어 공부해야봐야할 내용이고, 2번은 브라우저 지원범위를 보았을 때 완벽히 대응하기엔 어려움이 있다고 생각이 들었다. 더더욱 IE 를 버리지 못하는 한국에서는..

 

일단 Access Token 과 Refresh Token 을 활용하여 로그인 로직을 만드는 데에는 성공했으니, 이를 더 보완할 수 있는 법에 대해서는 차차 고민해보려 한다.

profile

파크로그

@파크park

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!