쿠키(Cookie)는 웹에서 빼놓을 수 없는 존재입니다. 가장 기본적인 인증에서부터 장바구니, 홍보, 유저 추적(Tracking) 등 여러가지 목적으로 광범위하게 사용되고 있습니다. 하지만 막상 사용하려고 하면 복잡한 설정 덕분에 쿠키가 제대로 저장/삭제되지 않거나 안전하지 않은 방식으로 사용하게 되기 일쑤입니다. 그렇기 때문에 이번 글에서는 쿠키에 사용할 수 있는 옵션에는 어떤 것들이 있으며 또 어떻게 하면 안전하게 쿠키를 사용할 수 있는 지를 알아보도록 하겠습니다.
백문이 불여일견이라는 말이 있죠?
cookie-playground로 들어가신 뒤 Getting Started
에 따라서 서버와 클라이언트를 띄우면 여러가지 옵션을 가진 쿠키를 직접 생성/삭제해볼 수 있는 playground
을 사용하실 수 있습니다.
한 번 직접 설치한 뒤 실행해볼까요?
위와 같은 화면이 나온다면 한 번 버튼을 눌러보면서 놀아보세요.
어떤 버튼을 누르면
이렇게 해당 쿠키의 내용 token=token
과 함께 여러가지 쿠키 옵션에 대한 설명이 나오는 것을 알 수 있습니다.
이제 쿠키 옵션들을 하나씩 살펴봅시다.
첫 번째 옵션은 secure
입니다. 이 옵션은 이름에서부터 짐작할 수 있듯이 안전한 연결(https)을 통할 때만 쿠키가 설정되는 옵션입니다. 우리가 일반적으로 사용하는 http는 암호화가 되지 않아 모든 통신 내용이 평문(clear text)으로 전달되기 때문에 보안이 취약합니다. 이때 secure
옵션을 가진 쿠키를 사용하게 되면 https 연결을 사용할 때만 쿠키가 설정되기 때문에 한결 안전하게 쿠키를 사용할 수 있습니다.
일반적으로 이 옵션은 개발환경의 경우 꺼두고, 프로덕션 환경에서만 사용하게 되는데요,
res.cookie(cookieName, cookieValue, {
secure: process.env.NODE_ENV === 'production'
})
와 같은 방식으로 사용할 수 있습니다.
domain과 path는 모두 경로와 연관된 옵션들입니다.
이 두 가지 옵션이 필요한 것은 쿠키가 유효한 범위를 지정하기 위해서 입니다.
한 번 domain = '.facebook.com'
이라는 옵션이 적힌 버튼을 눌러보세요. 아마 쿠키가 설정되지 않을 겁니다. 이는 우리의 도메인이 localhost
이기 때문인데요, 설정된 도메인인 .facebook.com
이 localhost
와 일치하지 않아서 그렇습니다.
다음은 path 옵션입니다. path는 해당 도메인의 하위 경로에 대한 범위를 지정해줍니다.
이번에는 path="/app"
이라고 되어 있는 버튼을 눌러봅시다.
마찬가지로 아무 일도 일어나지 않을 것입니다.
이때 localhost:3000/app
으로 이동하면
위와 같이 같은 이름과 값을 가진 두 개의 쿠키가 생성된 것을 알 수 있습니다.
이는 path 옵션이 유저가 현재 어떤 경로에 위치해 있느냐에 따라 접근가능한지 아닌지가 달라지기 때문입니다.
가장 기본적인 "/" 옵션은 모든 하위 경로에 대해 동작합니다. express
앱에서는 아무런 옵션을 지정해주지 않을 경우 path를 "/"로 설정하며, 이 경우 해당 도메인의 어떤 경로에서도 쿠키에접근할 수 있습니다.
그러므로 아까 쿠키가 두 개 보인 것은
cookie1 path=/
cookie2 path=/app
/app 경로에 있을 경우 /, /app 모두 유효함
위와 같이 되기 때문입니다.
위 두 옵션은 쿠키를 제거할 때도 마찬가지로 적용됩니다.
/
이 아닌 다른 경로로 이동한 뒤 Remove
버튼을 눌러도 쿠키가 제거되지 않는 이유는 해당 path가 설정된 쿠키만 제거할 수 있기 때문입니다.
secure
와 헷갈릴 수 있는 이름을 가진 옵션입니다.
하지만 이 옵션은 실제로는 http 통신을 통해서만 사용될 수 있는 쿠키라는 의미로, js를 통해 제어가 가능한지를 설정합니다.
httpOnly가 설정된 쿠키의 경우 js로는 접근이 불가능하며, 그렇기 때문에 xss 공격의 가능성을 줄여줍니다.
그러나 httpOnly 옵션을 설정해두었다고 해서 모든 종류의 xss 공격으로부터 자유로운 것은 아니면 기본적으로 xss 공격을 회피하는 것은 개발자의 책임입니다.
이름에서 알 수 있듯이 쿠키의 수명을 결정하는 옵션입니다.
통상적으로 maxAge = 0로 설정하여 쿠키를 제거합니다.
(쿠키의 경우 명시적으로 "제거"한다는 개념은 없으며 덮어쓰기를 통해 내용을 수정합니다.)
same-site
옵션은 가장 나중에 나온 옵션으로 csrf 공격을 방어하기 위한 옵션으로 사용될 수 있습니다.
현재는 세계적으로 89% 정도의 브라우저가 이 옵션을 지원하며, 여러가지 쿠키 옵션들 중에서도 가장 강력한 보안 옵션이라고 할 수 있습니다.
이 옵션의 기본적인 아이디어는 같은 도메인에서만 쿠키를 전달하거나(Strict) 안전한 요청(get
)을 통하는 경우에만 쿠키를 전달(Lax)하자는 것입니다.
이는 csrf 공격의 핵심이 로그인 사용자로 가장하여 위험한 동작을 실행시키는데 있다는 것을 생각해보면 가장 효과적인 방어책이라고 볼 수 있습니다.
일반적인 SPA에서 가장 많이 사용되는 http client는 fetch
혹은 axios
일 것입니다. 안타깝게도 이들은 기본 설정으로 서로 다른 origin 간에 쿠키를 전송하지 않습니다.
크롬의 경우 fetch의 기본 옵션은 credentials: same-origin
이며 domain과 달리 origin은 포트 번호까지 포함하는 개념이기 때문에 localhost:8080
과 localhost:3000
두 서버는 credentials
옵션을 변경하지 않고서는 cookie를 주고 받을 수 없습니다.
그렇기 때문에 현재 playground의 fetch 및 axios 설정은 각각
Axios -> withCredentials: true
Fetch -> credentials: include
로 되어 있습니다.
쿠키는 무상태인 http 통신 과정 중에 클라이언트에 상태를 저장할 수 있는 중요한 메커니즘 중 하나입니다. 그러나 다른 클라이언트로부터 넘어오는 값과 마찬가지로 절대로 신뢰할 수 없는 값이며, 서버측의 검증이 필수입니다.
여기에 더해 적절한 보안 옵션을 같이 설정해주게 된다면 쿠키를 더욱 안전하게 사용할 수 있습니다.
긴 글 읽어주셔서 감사합니다.
쿠키 문제 해결하다가 여기까지 왔네요 좋은 글 잘보고 가요 감사합니당