웹&네트워크 05. 쿠키와 세션 & 토큰

Sal Jeong·2022년 8월 17일
2

쿠키는 브라우져에 저장되고, 해당 쿠키로 서버에서 세션을 생성하고 서버의 inmemory에 저장하고...
해킹당하기 쉬우니까 JWT같은 token-based-authentication을 사용한다... 뭐가 더 있나?

1. 세션 로그인은 안좋다. 왜?

전 직장에서 React-Native 프론트 앱을 바라보는 django 서버가 자꾸 '터지는' 현상에 대해서 백엔드 개발자 한 분과 머리를 싸매고 고민한 적이 있는데, 이에 대해 근본적인 문제가 장고의 세션 로그인 방식인 'SessionAuthenticator'였다고 결론을 내린 적이 있다.

  1. Session은 해당 AWS 서버 인스턴스의 in memory나 바라보고 있는 db 내부 무한하게 늘어날 수 있기 때문에,

  2. 이 처리 용량이 오버되면서 rds에서 들어오는 모든 쿼리를 거부하게 되어

  3. 서버의 응답이 늦어지고,

  4. DB에서도 헬스체크에 실패하게 되어 Session_data 테이블을 날려버리게 되어

  5. 이때 로그인 정보가 모두 날아가게 되는 것이 주된 문제였다.

사실 JWT 인증 방식을 전에 구현해 보지 않은 것도 아니고(NodeJS였지만), 충분히 로그인 방식을 바꿀 수 있었다고 생각하는데, 프론트 단의 인증 방식 역시 바꿔야 해서 대수술이 되었기에 제대로 도전해 보지 않은 점이 아쉽다.

참고로, React-Native에서는 각 Native 별 쿠키 저장공간이 따로 있어서, 이 부분에서 자동으로 http requests를 날릴 때 header를 조작해 준다.

https://developer.android.com/reference/java/net/CookieStore

https://developer.apple.com/documentation/foundation/nshttpcookiestorage

안드로이드에서는 CookieStore, ios는 nshttpcookiestorage라는 이름이다.

어쨋거나 해당 sessionauthenticator를 token-based로 바꾸는 것이 장기적인 과제의 또 하나였다. Redux는 이미 구현이 되어 있었고, Redux-persist를 통해 persistable한 스토어를 AsyncStorage와 같은(암호화가 되면 더 좋고) 스토어에 넣어서 request 시마다 꺼내 쓰면 충분히 해결할 수 있었던 문제였다.

여기까지 정리해놓고 보니, 그렇다면 토큰은 무조건 만능인가? 하는 의문이 생긴다. 토큰은 가장 좋은 방식이기 때문에 내가 본 모든 OAuth에서는 access token과 refresh token을 내려주는 토큰 인증방식을 취하는 걸까?

2. 그렇다면 쿠키와 세션은?

쿠키란, MDN에 따르면 HTTP Cookie가 정식 명칭(web cookie, browser cookie) 이라고 한다.

일반적으로 서버에서 자동으로 생성되어 클라이언트에 전달, 클라이언트(browser나 mobile app)에 저장되고 해당 값을 클라이언트 쿠키 스토리지에 저장한 뒤, request 시마다 헤더에 넣어주는 것이다.

쿠키는 stateless가 원칙인 http 프로토콜에서 state를 저장하기 위한 방편중 하나로 아래의 토큰 방식과 비교된다.

Cookie based authentication의 절차는

  1. 유저의 로그인 요청이 일어남
  2. 서버가 해당 요청의 Credential 정보를 확인하면, db나 메모리에 Session을 형성함
  3. 서버에서는 응답으로 200 success와 함께, Set-Cookie 헤더에 Session ID를 보내줘서 클라이언트가 저장할 수 있도록 함.
HTTP/2.0 200 OK
Content-Type: text/html
Set-Cookie: <cookie-name>=<cookie-value>
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number>
Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain>
Set-Cookie: <cookie-name>=<cookie-value>; Path=<path>
Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

[page content]

이러한 식으로 쿠키의 요소를 확인할 수 있다.

  1. 브라우저에서는 해당 도메인 기반으로 이루어지는 요청에 위 Cookie를 request header에 넣어서 보낸다.

  2. 유저가 로그아웃하면, 서버는 db나 메모리에 저장된 세션 자체를 삭제한뒤 응답을 보내고 클라이언트에서도 해당 쿠키를 삭제한다.

쿠키 로그인 방식의 장점과 단점

클라이언트에서 해주기 때문에, 추가로 개발을 할 필요가 없는 것이 최고의 장점이다.

하지만 그렇기 때문에 XSS와 CSRF(Cross-Site Request Fogery)와 같은 공격에 취약하다.

이러한 보안상 취약점을 막기위해,

Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax

서버에서는 SameSite=strict 과 같은 방식으로 막을 수 있다고는 하는데, 직접 사용해보지 않아 모르겠다.

그리고 가장 큰 단점은, 위에서 내가 경험해 보았듯이

db나 메모리에 세션이 무한히 늘어날 수 있어서 scalability 이슈가 일어난다는 것이었다.

3. 토큰 인증 방식은 어떨까?

토큰은 쿠키와는 다르게, 직접 코드를 통해 응답에서 가져와서 어딘가에 저장해 놓아야 한다.

이 중 JWT(JSON WEB TOKEN)이 어떻게 동작하는지의 절차는

  1. 클라이언트가 credential을 입력받아 인증 요청을 보낸다.
  2. 서버는 credential을 대조하고, 맞다면 서버에 저장된 secret 값으로 사이닝된 토큰을 생성한다.
// Install
npm install jsonwebtoken
// Usage
var jwt = require('jsonwebtoken');
var token = jwt.sign(
              { data: user}, 
              privateKey, 
              { algorithm: 'RS256'},
              exp: Math.floor(Date.now() / 1000) + (60 * 60),            );

express에서의 사용 예

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

https://jwt.io/
에서 생성된 토큰의 모습과, 실제 들어있는 데이터의 형태를 볼 수 있다.

  1. 해당 토큰은 브라우저 스토리지에 저장하고, 자바스크립트를 통해 요청을 보낼 때 일반적으로 Authorization 헤더에 추가한다.

  2. 인증이 만료되면, 직접 코드로 해당 토큰을 삭제한다.

토큰 로그인 방식의 장점 단점

쿠키/세션과 비교한 가장 큰 차이는

stateless 하다. - 서버에서 세션을 어떤 방식으로든 저장하는 것이 아니라 오로지 토큰 생성 + 인증 작업만 하기 때문에, 쿠키/세션 방식에 비해 Scalibility 이슈에 자유롭다.

하지만 token 자체가 어떻게든 유출될 경우에는 쿠키와 마찬가지로 보안상 이슈가 있고, 이것을 XSS(Cross Site Scripting) 공격이라고 부른다.

따라서, 확장성만을 생각하면 토큰 방식을 쓰는 것이 맞는 것 같다.

참고한 URL

https://locastic.com/blog/react-native-cookie-based-authentication

https://rossbulat.medium.com/how-to-use-redux-persist-in-react-native-3b0d912e730a

https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
profile
행복하기 위해 코딩합니다. JS 합니다.

0개의 댓글