LeChuck

쿠키와 세션, 토큰

·5 min to read

서버 입장에서는 클라이언트에서 보내는 무수히 많은 요청들이 누구의 것인지 분별하기가 어렵다. 쿠키와 세션은 요청을 보내는 클라이언트가 누구인지를 판별해내기 위해 사용된다. 쿠키와 세션을 통해 한 번 인증(로그인)된 사용자는 쿠키와 세션이 만료되기 전까지 새로고침 등으로 연결이 끊기더라도 유효함이 보장된다.

HTTP는 Stateless하기 때문에 모든 요청-응답 쌍(connection)은 독립적이며, 요청 간 정보가 유지 공유되지 않는다. 따라서 내가 이전에 사용자 정보를 담아서 요청을 보낸 서버였더라도 요청이 끝난 시점에 서버는 내 정보를 잊는다. 요청할 때마다 서버에 내 정보를 알려주기 위해서 쿠키 등을 사용한다.

인증 Authentication (로그인) 인가 Authorization -> 인증받은 사용자에게 서비스 이용 권한을 부여하는 것.

쿠키

서버는 요청에 대한 응답을 할 때(2번) 쿠키를 같이 보내서 사용자를 기억한다. 쿠키는 유효 기간이 있고, '키=값' 꼴의 문자열이다. 웹 브라우저는 서버로부터 받은 이 쿠키를 저장해두었다가 요청할 때마다 쿠키를 동봉해서 보내고, 서버는 이 쿠키를 통해 사용자를 파악한다. 즉, 서버로부터 쿠키를 발급받기 전인 클라이언트의 첫 번째 요청에는 쿠키가 없겠지만, 두 번째 요청부터는 쿠키가 동봉되기 때문에 서버는 이 사용자를 식별할 수 있다.

서버에서 브라우저로 쿠키를 보내는 코드를 작성하기만 하면 쿠키가 적용된다. 브라우저는 쿠키가 있다면 자동으로 쿠키를 서버에 보내기 때문에 따로 신경쓸 필요가 없다. 서버에서 Response Header에 Set-Cookie 속성을 지정해서 쿠키를 만들 수 있다.

<script>
 
// Response Header에 Set-Cookie 속성을 지정하여 쿠키를 만들고, 클라이언트에 전송하는 모습.
http.createServer((req, res) => {
	res.writeHead(200, {'Set-Cookie': 'mycookie=test' });
    res.end('Hello Cookie');
})
    
</script>
<script>
 
// 이번에는 개별 사용자를 식별할 수 있게끔 쿠키에 name 정보를 집어넣고, 옵션들을 설정한 모습.
res.writeHead(302, {
            Location: '/',
            'Set-Cookie': `name=${encodeURIComponent(
                name
            )}; Expires = ${expires.toGMTString()}; HttpOnly; Path=/`,
        });
        
</script>
  • 쿠키는 브라우저(로컬)에 저장되는 키=값 형태(문자열)의 작은 데이터 파일이다.

  • 쿠키는 요청과 응답 각각의 헤더에 담겨 오고 간다.

  • 쿠키는 도메인별로 관리된다. 네이버가 발급한 쿠키는 (요청시) 네이버에만 보내지게 된다.

  • 쿠키 간에는 세미콜론으로 구분된다.

  • 쿠키는 유효기간 내에서는 브라우저가 종료되어도 유지된다.

쿠키는 위와 같이 노출되어 있다.

웹 스토리지

쿠키의 문제점을 해결하기 위해 HTML5에 등장. 웹 스토리지는 로컬 스토리지와 세션 스토리지로 나뉜다. 웹 스토리지는 브라우저별로 지원하지 않는 경우가 있어(사파리 시크릿 모드) 에러 처리가 동반되어야 한다.

  • 로컬 스토리지 -> 도메인/브라우저, 직접 삭제 시 삭제된다.
  • 세션 스토리지 -> 도메인/브라우저/탭, 탭 종료 시 삭제된다.

세션

쿠키

  • 클라이언트에 저장

  • 유효기간 내에서는 브라우저가 종료되도 쿠키가 유지

세션

  • 서버(DB)에 저장. 장) 보안에 유리. 단) 서버 메모리에 부담

  • 유효기간에 상관없이 브라우저가 종료되면 세션도 삭제 (세션 쿠키인 경우에는???)

  • 클라이언트에 고유 세션 ID를 부여하고(주로 쿠키를 클라이언트에 줌으로써), 이 세션 ID를 통해서만 클라이언트와 소통하여 서버에서 관리하기 때문에 사용자 정보가 노출되지 않음. 즉, 모든 정보는 서버가 소유하고 클라이언트는 세션 ID만 소유함. (보안)

  • 요청이 들어올 때마다 서버는 쿠키(세션 ID)를 받아보고, DB에서 세션 ID와 일치하는 사용자 정보를 찾는 과정을 거쳐야 한다. 따라서 유저가 늘어날수록 DB 리소스가 늘어나야 한다. (서버 메모리)

세션은 꼭 쿠키를 이용하지 않아도 되지만, 간편하기 때문에 보통 쿠키를 사용해서 세션을 주고받는다. 이처럼 세션을 위해 사용하는 쿠키를 세션 쿠키라고 부른다.

<script>
// 위 쿠키 예제에서의 name처럼 직접적인 정보가 노출되지 않고, 고유한 숫자 ID만이 노출된다.
// 실제 서버에서는 세션을 아래와 같이 변수에 저장하지 않는다. DB에 둔다.
 res.writeHead(302, {
            Location: '/',
            'Set-Cookie': `session=${uniqueInt}; Expires = ${expires.toGMTString()}; HttpOnly; Path=/`,
        });
</script>
 

세션 아이디 값이 공개되어 있는 위 서비스도 보안상 매우 취약한 것.

240p express-session, 9.3절 express-session을 이용한 로그인 예제.

토큰

IOS, Android에는 쿠키가 없다. 따라서 토큰을 사용한다. 토큰은 문자열이고, 이 토큰을 서버 내 세션 DB에 보내서 검증하는 것. (쿠키와 사용방법은 비슷)

JWT

토큰 형식. JWT를 사용하면 세션 DB가 필요 없다. 이는 서버의 부담을 줄여준다.

유저 인증에 필요한 정보를 session DB가 아닌 token에 저장한다. 사용자가 id와 pw를 입력하여 로그인 요청을 보내면 서버는 token을 생성하는데, 이 token은 유저 ID + sign algorithm을 활용해서 만든 string 형태의 signed Info다. 그리고 이 token(signed info)을 client에 전달한다. 이후 요청이 들어오면 서버는 해당 토큰이 유효한지만 검증한다. 세션이 DB에 저장되는 반면 토큰은 클라이언트 측에만 저장되는 셈이다.

  • 활용 사례 : QR 코드

References

쿠키와 세션 개념

세션 vs 토큰 vs 쿠키? 기초개념 잡아드림. 10분 순삭!

<Node.js 교과서>(2020, 조현영)