Cookie & Session vs JWT

인터넷 상에서의 웹통신에는 여러가지 규약이 존재한다. 이것을 Protocol이라고 하는데, HTTP는 서버와 클라이언트 모델을 따르는 가장 기본적인 Protocol이다.

HTTPconnectionlessstateless 특징을 가지고 있어서 요청에 대한 응답을 처리하게 되면 연결을 끊어 버린다. 따라서 클라이언트에 대한 이전의 상태 정보 및 현재 통신의 상태가 남아있지 않는다.

이러한 HTTP의 특징은 서버가 다수의 클라이언트와 연결을 계속 유지했을때 발생하는 자원 낭비를 줄일 수 있지만, 클라이언트를 식별할 수 없어 로그인을 하더라도 요청을 다시 보낼때 마다 새로 인증을 해야하는 상황이 발생한다.

이런 문제점들을 보완하기 위한 기술이 Cookie & Session 이다.

1. Cookie 인증

Cookie는 서버가 사용자의 웹 브라우저에 저장하는 작은 데이터이다. 각각의 브라우저는 서버로 부터 받은 Cookie를 저장하고 있다가 동일한 서버에 요청시 저장된 Cookie를 헤더에 실어 보낸다. 이를 통해 서버는 Cookie에 있는 개인정보를 보고 사용자의 인증여부를 확인한다.

Cookie 속성

1. Domain

Cookie는 기본적으로 Cookie가 생성된 서버로만 전송된다. 예를 들어 유튜브에서 생성된 Cookie는 유튜브로 요청시에만 전송된다. 하지만 여러개의 웹서버에서 Cookie를 공유하고 싶을때는 Domain 속성을 사용할 수 있다.

Domain 속성은 Cookie가 어떤 서버로 전송되어져야 하는지 지정할 수 있는 속성으로 값으로는 현재 서버의 주소 혹은 상위 도메인까지만 지정할 수 있다. 예를 들어 유튜브에서 생성된 Cookie의 Domain은 www.youtube.com 혹은 youtube.com까지만 지정할 수 있다. google.com등 다른 서버의 주소를 도메인으로 설정하는 것은 불가능하다. 만약 속성을 지정하지 않을 경우 오직 생성된 서버로만 전송되고, 서브 도메인에서도 Cookie를 사용할 수 없다.

2. Path

Cookie의 Path 속성을 이용하면 웹서버의 특정 URL에 대해서만 쿠키를 전송할 수 있다. Path 속성은 웹서버의 디렉토리 단위로 지정이 가능하며, 디렉토리를 지정하면 지정한 해당 디렉토리와 그 하위 경로에만 쿠키가 전달된다. 예를 들어 Path 값을 /subDir1/ 으로 지정하면 localhost:3000/subDir1/xxx.html 와 같이 subDir1 하위 경로로 요청시에만 쿠키가 전송된다.

3. Expires, Max-age

Expires 속성과 Max-age 속성은 Cookie의 최대 생존 시간으로, 값은 시간으로 입력하며 보안과 사용자의 편의성을 고려해 적절하게 설정하는 것이 좋다. 따라서 Cookie를 삭제하고 싶을때는 Expires 속성을 지난 시간으로 설정하면 된다.

만약 이 속성들이 설정되어 있지 않다면 브라우저가 닫힐 때 쿠키도 함께 삭제되는데, 이러한 Cookie를 session cookie 라고 부른다.

4. HttpOnly

위에서 보았듯이 HTTP는 기본적으로 사용자의 상태 정보를 기억하지 못하기 때문에 Cookie를 이용하여 사용자를 식별한다. 따라서 Cookie에는 사용자를 구분하는 개인정보가 담겨있고, 만약 해커들이 쿠키를 탈취하게 되면 해커는 탈취한 Cookie를 이용하여 해당 사용자로 위장을 할 수 있다. 이 때문에 보안이 굉장히 중요하다.

HttpOnly속성은 Cookie 하이재킹 방법중 하나인 CSS(Cross Site Scripting)을 방지하기 위한 속성이다. CSS는 클라이언트에서 자바스크립트로 쿠키를 조회하여 Cookie를 탈취하는 해킹 방법이다. HttpOnly속성을 설정하게 되면 클라이언트에서 자바스크립트로 Cookie에 접근하지 못하게 하여 해킹을 방지할 수 있다.

5. Secure

HttpOnly속성을 사용하게 되면 클라이언트에서 Cookie를 탈취하는 것을 막을 수 있지만, 만약 네트워크를 감청하여 쿠키를 가로챈다면 HttpOnly속성으로는 이를 방지할 수 없다.

HTTPS 프로토콜은 이런 통신상의 정보유출을 막기 위해 데이터를 암호화하여 통신을 주고받는 방법으로, 이는 제3자가 데이터를 확인할 수 없게 한다. Secure속성은 오직 HTTPS 통신에서만 쿠키를 전송하게 하는 속성으로, 의도치 않게 HTTP 통신을 통해 유출되는 것을 막아준다.

6. Samesite

Samesite속성은 CSRF(Cross Site Request Forgery) 공격을 막기 위한 속성이다. CSRF 공격은 사용자가 유효한 Cookie를 가지고 있을때 의도치 않게 <form action="https://bank.com/pay"> 과 같은 태그가 있는 해킹사이트를 접근하면, 이 해킹사이트는 사용자의 Cookie와 함께 bank.com 사이트에 요청을 보내게 되고, 서버는 Cookie를 통해 사용자로 인식하게 된다.

위와 같은 해킹을 막기위해 Samesite속성이 설정하면 제3의 도메인에서 요청이 이뤄질 때 쿠키가 전송되지 않는다. Samesite속성의 값으로는 strictlax를 설정할 수 있는데 strictSamesite속성의 디폴트 값이다. laxstrict보다 조금 느슨한 방법이다. 이는 strict를 설정했을때 XSRF 공격을 막을 수 있지만 사용자의 경험을 해치는 문제를 해결하기 위해 사용하는 값으로, strict보다 사용자 경험을 해치지 않으면서 XSRF 공격을 막을 수 있다. 더 자세한 내용은 이 사이트를 확인하면 된다.

Cookie 단점

1. 보안 문제

Cookie는 클라이언트에서 존재하다가 서버로 전송되고, 서버에서는 이를 이용해 클라이언트의 정보를 확인함으로 XSS,XSRF와 같이 Cookie를 탈취하는 해킹방법으로 Cookie를 해킹 당할 수 있다.

비록 해킹을 방지하기위해 Cookie를 설정할 때 Samesite, Secure, HttpOnly과 같은 옵션을 설정할 수 있지만, HTTP로 개인정보를 주고 받는 것은 위험하다.

2. 용량 제한

Cookie는 클라이언트에 총 300개, 하나의 도메인 당 20개, 하나의 Cookie 당 4KB까지만 저장 가능하여 많은 정보를 담지 못한다.

3. 네트워크 부하

Cookie의 사이즈가 커지면 커질수록 서버와 클라이언트 간 통신하는 데이터가 많아져 네트워크에 부하가 심해진다.

4. 호환성

웹 브라우저마다 쿠키에 대한 지원 형태가 다르기 때문에 브라우저 간 공유가 불가능하다.

2. Cookie & Session 인증

Session이란 웹 사이트의 여러 페이지에 걸쳐 사용되는 사용자 정보를 저장하는 방법 중 하나이다. 사용자가 브라우저를 닫아 서버와의 연결을 끝내는 시점까지를 Session이라고 한다.

앞서 살펴본 Cookie는 클라이언트에 모든 데이터를 저장하지만 Session은 서버의 Session DB에 데이터를 저장하고, Session의 id값만 Cookie를 통해 클라이언트에 남겨둔다.

클라이언트는 서버에 요청을 보낼때 Session의 id값을 가지고 있는 Cookie 도 같이 전송하게 되고, 서버는 Cookie에 저장되어 있는 Session id를 이용하여 Session DB에서 개인정보 및 인증여부 등을 확인하고 클라이언트에 적절한 response를 보낸다.

Session 장점

1. 보안

Cookie만을 이용한 인증에서는 개인정보가 Cookie에 전부 담겨 있지만, Session의 경우 Cookie에는 Session의 id값만 있고 유의미한 데이터가 없어서 Cookie에 비해 보안이 더 뛰어나다.

2. 용량

Cookie의 경우 용량 제한이 있지만, Session의 경우 서버가 허용하는 한 용량 제한이 없어 더 큰 데이터를 저장할 수 있다.

3. 네트워크

Cookie의 경우 저장하는 데이터가 클수록 네트워크에 부하가 커지지만, Session의 경우 저장하는 데이터가 커져도 Cookie에 저장되어 있는 데이터는 Session id값 밖에 없으므로 네트워크에 영향을 미치지 않는다.

Session 단점

1. 확실하지 않은 보안

비록 Cookie에는 유의미한 데이터가 없어도, 해커가 Cookie를 탈취하여 해당 사용자인척 위장할 수 있다는 한계가 있다.

2. 서버 문제

사용자의 정보를 서버의 Session DB에 저장함으로 요청이 많아지면 서버에 부하가 심해진다.

3. 속도

Session 인증은 Session DB를 거쳐야 사용자의 정보를 파악할 수 있으므로, Session DB를 거치지 않는 Cookie 인증보다 속도가 느리다.

JWT 인증

JWT(JSON Web Token) 란 인증에 필요한 개인정보들을 암호화시킨 토큰을 의미한다. JWT를 이용한 인증은 Cookie & Session 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별한다.

JWT 구조


JWT는 .을 기준으로 세부분으로 나누어진 문자열이고, 이는 Header, Payload, Signature 으로 나누어진다.

1. Header

첫번째 부분은 Header이고, algtyp으로 구성된다. alg은 암호화할 해싱 알고리즘에 대한 정보가 담겨있고, typ은 토큰의 타입에 대한 정보가 담겨있다.

2. Payload


두번째 부분은 Payload이고, 토큰에서 사용할 정보인 클레임(Claim)이 담겨 있다. 이 클레임은 총 3가지로 나누어진다.

2-1. 등록된 클레임

등록된 클레임은 토큰 정보를 표현하기 위해 이미 정해져 있는 여러 종류의 데이터이다.

iss: 토큰 발급자(issuer)
sub: 토큰 제목(subject)
aud: 토큰 대상자(audience)
exp: 토큰 만료 시간(expiration), NumericDate 형식으로 되어 있어야 함
nbf: 토큰 활성 날짜(not before), 이 날이 지나기 전의 토큰은 활성화되지 않음
iat: 토큰 발급 시간(issued at), 토큰 발급 이후의 경과 시간을 알 수 있음
jti: JWT 토큰 식별자(JWT id), 중복 방지를 위해 사용하며, 일회용 토큰(Access Token) 등에 사용

2-2 공개 클레임

공개 클레임은 사용자 정의 클레임으로, 공개용 정보를 위해 사용된다. 충돌 방지를 위해 uri 포맷을 이용한다.

{
    "https://xxxx.com/xxx/xxx" : ture
}

2-3 비공개 클레임

비공개 클레임은 사용자 정의 클레임으로, 서버와 클라이언트 사이에 임의로 지정한 정보를 저장한다.

{ 
	"token_type": access
}

3. Signature

세번째 부분은 Signature이고, 인코딩된 Header와 Payload를 더한 뒤 비밀키로 해싱하여 생성한다. Header와 Payload는 단순히 인코딩된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다. 따라서 Signature는 토큰의 위변조 여부를 확인하는데 사용된다.

JWT 인증 과정

  1. 클라이언트에서 로그인 요청이 들어오면, 서버는 검증 후 클라이언트 고유 ID 등의 정보를 Payload에 담는다.
  2. 암호화할 비밀키를 사용해 JWT를 발급하고 클라이언트에게 전달한다.
  3. 클라이언트는 전달받은 토큰을 저장해두고, 서버에 요청할 때 마다 토큰을 헤더 Authorization에 포함시켜 함께 전달한다.
  4. 서버는 토큰의 Signature를 비밀키로 복호화한 다음, 위변조 여부 및 유효 기간 등을 확인하고 유효한 토큰이라면 요청에 적절한 response를 한다.

JWT 장점

  1. 서버의 비밀키를 이용하여 Signature를 생성하므로 데이터 위변조를 막을 수 있습니다.
  2. 사용자 인증에 필요한 모든 정보는 토큰 자체에 포함하기 때문에 Session DB처럼 저장소가 필요없다.
  3. 인증을 할때 Cookie를 전달하지 않아도 됨으로 Cookie를 전달했을때 발생하는 보안문제가 발생하지 않는다
  4. 모든 브라우저에서 사용가능 함으로 호환성이 뛰어나다.
  5. 모바일 어플리케이션 환경에서도 사용가능하다.

JWT 단점

  1. Cookie & Session과 다르게 JWT는 토큰의 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해진다.
  2. Payload 자체는 암호화되지 않기 때문에 유저의 중요한 정보는 담을 수 없다.
  3. JWT는 한번 발급하면 유효기간이 만료될 때 까지 계속 사용가능하기 때문에 JWT를 탈취당하면 대처하기 어렵고, 특정 사용자의 접속을 강제로 만료하기 어렵다.

JWT 보안 전략

1. 짧은 만료 기한 설정

토큰의 만료 시간을 짧게 설정하여 토큰이 탈취되더라도 빠르게 만료되어 피해를 최소화할 수 있다. 하지만 사용자가 자주 로그인해야 하는 불편함이 수반된다.

2. Refresh Token

클라이언트가 로그인 요청을 보내면 서버는 Access Token 및 그보다 긴 만료 기간을 가진 Refresh Token을 발급한다. 클라이언트는 Access Token이 만료되었을 때 Refresh Token을 사용하여 Access Token의 재발급을 요청한다. 서버는 DB에 저장된 Refresh Token과 비교하여 유효한 경우 새로운 Access Token을 발급하고, 만료된 경우 사용자에게 다시 로그인을 요구한다.

이 경우 Access Token의 만료 기한을 짧게 설정할 수 있으며, 사용자가 자주 로그인할 필요도 없다. 또한 서버가 강제로 특정 사용자의 Refresh Token을 만료시킬 수 있다.

그러나 검증을 위해 서버는 Refresh Token을 별도의 DB에 저장해야 한다. 이는 추가적인 I/O 작업이 발생함을 의미하기 때문에 JWT의 장점(I/O 작업이 필요 없는 빠른 인증 처리)을 완벽하게 누릴 수 없다.

References

  1. https://tecoble.techcourse.co.kr/post/2021-05-22-cookie-session-jwt/
  2. https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
  3. https://ko.javascript.info/cookie
  4. https://nsinc.tistory.com/121
  5. https://mangkyu.tistory.com/56

좋은 웹페이지 즐겨찾기