image


HTTP의 특징

connectionless, stateless

HTTP 프로토콜 환경은 비연결성(connectionless), 무상태성(stateless)을 띕니다.

✅ connectionless

response 보낸 뒤 연결 해제

HTTP는 클라이언트의 요청에 응답을 보내면 연결을 끊어버립니다.

✅ stateless

통신이 끊기면 상태정보 유지 안함

이후 연결이 끊기는 순간 클라이언트와 서버의 통신이 끝나고, 상태정보는 유지되지 않습니다.

https://user-images.githubusercontent.com/79133602/178751913-cc6da344-10a5-4171-bc58-86e73984da20.png

그래서 위 특성을 보완하지 않고 웹을 만든다면 로그인을 했음에도 사용자 정보가 남지 않아 페이지를 이동할 때마다 로그인을 해야되는 문제등이 발생할 것입니다.


그럼 어떻게 보완할까

쿠키, 세션을 사용

쿠키와 세션은 서버가 클라이언트 정보를 매번 확인할 필요가 없도록 상태정보를 유지해줍니다. 다만 저장위치, 보안면에서 차이가 있기에 그 부분을 고려해 사용해야 합니다.



쿠키

브라우저에 저장하는 데이터 파일

쿠키는 사용자의 상태 정보를 서버로 부터 받아 로컬에 저장해두고 쓰는 데이터 파일입니다. 서버는 Response Header의 Set-Cookie 속성을 사용해 쿠키를 만드는데, 무작위로 쿠키를 생성해버리면 네트워크 트래픽을 유발하기 때문에 갯수와 용량에 제한이 있습니다.

  • 클라이언트에 300개까지 저장가능
  • 도메인 하나당 20개까지 값을 가질 수 있음
  • 쿠키 하나당 최대 용량 4KB

그리고 response와 함께 쿠키를 클라이언트에 내려주고, 해당 쿠키는 브라우저에 저장됩니다.


브라우저 저장 이후

쿠키는 사용자가 따로 요청을 하지 않아도 Request 시 Request Header에 넣어서 자동으로 서버에 전송됩니다. 따라서 login 후 다른 페이지에서 요청을 할 때도 가지고 있던 쿠키가 자동으로 서버로 전송되서 사용자 정보를 매번 갱신할 필요가 없습니다.

https://user-images.githubusercontent.com/79133602/178756922-b92945a5-9556-4e76-84d2-09acc23548f1.png



쿠키 작동 방식

즉 다음과 같은 순서로 작동을 합니다.

  1. 클라이언트가 request를 보냄
  2. 서버에서 쿠키 생성
  3. HTTP헤더에 쿠키를 넣어서 response 전달
  4. Expires 기간만큼 브라우저에 저장
  5. 쿠키의 Path에서 request가 발생하면 자동으로 HTTP 헤더에 담김
  6. 서버는 쿠키를 보고 사용자 정보 확인후 response
  7. 쿠키에 업데이트가 필요하다면 업데이트 후 전송

그렇다면 쿠키엔 어떤 것들이 담겨있고 어떤 종류들이 있을까요?


예시) 깃허브 쿠키목록

깃허브의 쿠키 목록을 보면서 알아보자

https://user-images.githubusercontent.com/79133602/178752409-9112cb57-f5a9-4733-aa9f-7d44621337c1.png

사이트 접속 후 개발자 도구의 애플리케이션의 쿠키 메뉴를 보면 해당 도메인의 쿠키 정보를 알 수 있습니다. 위는 우리가 자주 사용하는 github의 쿠키 목록인데요. 보면 로그인 여부, 위치, 다크모드 사용여부, 아이디 등 사이트를 이용할 때 필요한 사용자 정보들을 담고 있습니다.


쿠키의 구성 요소

자세히 보면 다음과 같은 요소들로 이뤄진 걸 볼 수 있습니다.

https://user-images.githubusercontent.com/79133602/178758717-30af9473-7ec9-4f04-a9b9-2b657603cd3b.png

주요 속성은 다음과 같습니다.



1. name, value, size

각각 쿠키의 이름, 값, 용량입니다.

2. Path

Path는 해당 경로일 경우 쿠키가 전송된다는 의미로 현재 github 쿠키들의 Path는 ‘/’인데 이 경우 모든 요청에 쿠키가 전송됩니다. 만약 경로 설정을 안하고 만들었다면 쿠키를 만든 페이지의 경로가 쿠키의 경로가 됩니다.

https://github.com/bellasimi/bellasimi/settings

즉 해당 사용자의 설정을 바꾸는 위 경로에서 만든 쿠키는 해당 페이지의 경로의 요청에서만 사용됩니다.

3. HttpOnly

HttpOnly 속성의 경우 XSS공격을 막기 위해 만들어졌습니다. HttpOnly로 설정할 경우 HTTP 외 다른 통신을 사용할 수 없어서 스크립트를 통한 탈취가 불가능합니다. 다만, 브라우저에서 해당값에 접근할 일이 있고, 보안이 필요한 정보가 아니라면 해당 설정이 필요하지 않습니다.

그래서 선호하는 색, 시간대같은 정보는 스크립트로 조회가 가능합니다.

https://user-images.githubusercontent.com/79133602/178766689-68fd2da9-f9ab-42ba-8586-e3ee5824adee.png

4. Secure

Secure도 마찬가지로 보안을 위한 속성입니다. 스크립트 탈취 외 통신상의 정보 유출을 막기 위해 암호화된 HTTP 프로토콜 (HTTPS)인 경우만 쿠키를 전송합니다.

5. SameSite

마지막으로 SameSite는 Third-party Cookie와 연관이 있는 속성으로, 현재 도메인에서 다른 도메인에 요청을 하는 경우 전송여부를 결정합니다. 이는 아래 Third-party Cookie를 설명하면서 자세히 다루도록 하겠습니다.




쿠키 종류

쿠키는 종류에 따라 특성이 다르기 때문에 필요에 따라 선택해서 사용해야 합니다.

보통 쿠키하면 브라우저 종료 후에도 남아있는 Persistent Cookie를 쿠키를 떠올립니다. 하지만 그 외에도 쿠키정보를 암호화해서 HTTPS에서만 사용하는 Secure Cookie, 유입 경로를 추적하기 위해 사용하는 Third-Party Cookie, 그리고 쿠키와 별개인 걸로 많이 오해하지만 사실 세션이 사용하는 쿠키 Session Cookie가 있습니다.


현재 접속한 사이트가 아닌 다른 사이트를 domain값으로 두고 있는 쿠키입니다.

왜 다른 사이트 쿠키가 있어?

아래처럼 다른 사이트에서 이미지를 요청하거나 다른 사이트로 이동하는 경우 해당 사이트의 쿠키가 필요할 수 있기 때문입니다. 이 경우 깃허브에 있어도 other.com의 쿠키를 요청에 담아 보냅니다.

<img src="<https://other.com/image.png>"/>
<a href="<https://other.com>"/>


Third-Party Cookie를 악용한 CSRF공격

Cross Site Request Forgery는 위 특성을 악용한 웹 공격입니다. 예를 들어 누군가 그럴싸한 사이트를 만들고 여러분이 꾐에 넘어가 들어갔다고 가정해보겠습니다. 아래 제가 팀프로젝트로 만든 사이트인데요.

https://user-images.githubusercontent.com/79133602/178775127-abbbe90e-446b-47fa-9680-16c0fc4f29f1.png

여러분에게 순수한 식물 sns인 척 속이고 이미지 태그에 몰래 깃허브 비빌번호 변경 요청을 넣어버리면 여러분이 접속한 순간 비밀번호가 ‘every1111!’로 변경이 될 겁니다.

<img src="<https://github.com/changepassword?newPassword=every1111!"> /> //예시 url

그럼 이제 여러분의 id를 알고있는 저는 바뀐 비밀번호로 여러분의 깃허브에 들어가서 레포지토리를 전부 삭제해 버릴 수도 있습니다.

생각만 해도 끔찍해 어떡하지?

하지만 이보다 더 끔찍한 범죄도 가능합니다. 금융정보를 가로챈다면 돈까지 훔칠 수 있으니까요.


CSRF 방어

쿠키의 SameSite 속성

그래서 이를 막기 위해 SameSite 속성이 존재합니다. 속성값은 3가지가 있습니다.

  • None: 크로스 사이트 요청에도 항상 전송되기에 보안이 좋지 않습니다.
  • Strict: 크로스 사이트 요청을 차단합니다. Third-Party Cookie일 땐 전송이 되지 않도록 막아두기에 CSRF 공격을 방어할 수 있습니다.
  • Lax: Third-Party Cookie일 땐 대체로 전송되지 않지만 안전한 요청일 땐 허락합니다.

github의 쿠키들도 Lax 속성인데요. 이 쿠키들은 서드 파티쿠키일 때 안전하지 않은 post, delete, iframe, img 안의 HTTP요청은 수행하지 않습니다.



세션

쿠키를 사용하긴 해 근데 좀 달라

위에서 언급한 대로 세션 역시 Session Cookie라는 쿠키를 사용합니다. 그렇지만 다른 쿠키들과 차이점이 있기에 따로 분류해서 언급합니다.


세션의 차이점

먼저 사용자 정보의 저장 위치가 다른데요. 사용자 정보를 직접 받아 브라우저에 저장하고 사용하는 쿠키들과 달리, 세션은 실제 사용자 정보는 서버에 존재하고 해당 정보에 접근하기 위한 SessionID만을 쿠키로 전달 받아 저장합니다.

덕분에 서버 용량이 허용하는 선까진 제한이 없습니다.


작동 방식도 조금 달라

세션쿠키엔 Session ID뿐…

image

그리고 SessionID만 쿠키로 받아서 사용하기 때문에 서버에 요청을 보낼 때 해당 ID로 사용자 정보에 접근해야 합니다. 즉, 자동로그인을 할 때 다른 쿠키라면 브라우저에 저장된 아이디와 비번을 사용하겠지만 세션쿠키는 SessionID로 서버에서 사용자 정보를 받아 요청을 처리하게 될 겁니다.

서버에서 처리할 작업이 늘어나기 때문에 성능과 속도는 상대적으로 나쁠 수밖에 없습니다. 게다가 동접수가 많으면 서버 과부화가 발생합니다. 하지만 비번같은 중요한 정보를 숨길 수 있어 보안이 필요한 정보는 세션쿠키를 사용하는 게 좋습니다.



번외 : 프론트엔드 개발자와 쿠키

백엔드와 프론트엔드의 개발서버가 다를 때처럼 도메인이 다른 경우 쿠키 전송이 자동으로 되지 않습니다(CORS). 이럴 떈 프로시설정을 한 뒤 다음과 같이 해결해봅시다!

쿠키를 못 받아오는 경우

일단 해당 요청에 axios.get(주소, { withCredentials: true }); 이런식으로 credential 정보 CORS가 가능하도록 설정했는지 확인합니다.


했는데?

이럴 땐 백엔드 개발자에게 달려갑시다. 쿠키는 서버의 CORS 정책에 따라 내려오지 않을 수 있습니다. 백엔드 개발자 분의 security 관련 코드 중 CORS 설정을 보면 분명 다음 중 하나라도 잘 못된 값이 있을 겁니다. 그럼 그 부분을 고쳐달라고 하면됩니다.

  • Origin: 보안이 필요한 정보를 내려줄 땐 ‘*’ 이면 안됩니다.
  • Access-Control-Allow-Credentials: credential에 접근할 수 있도록 true여야 합니다. 해당 속성은 프론트엔드의 요청 헤더 withCredentials와 이어집니다.


그래도 안되는데?

그러면 쿠키 설정을 봐야 합니다.쿠키는 보안을 위해 set-Cookie를 할 때 Secure 속성은 true, SameSite 속성은 strict 또는 Lax로 줬을 겁니다. 그러면 로컬서버에선 사용할 수 없습니다. 로컬서버는 http이기 때문입니다. Secure 속성을 false로 주고 개발 서버에서 테스트 해볼 수 있겠지만 찝찝하다면 그 떈 mkcert라는 인증서 발급 프로그램으로 로컬 서버를 https로 변경해 이부분을 해결할 수 있습니다.


더 쉬운 방법

프록시 설정 이후 서버가 SameSite로 인식하지 못 하면 Secure: false에도 쿠키가 내려오지 않습니다. 떄문에 배포를 해서 프론트와 백엔드가 공유하는 도메인 값을 쿠키에 넣고 테스트 해보는 게 더 수월할 것입니다.



참조

💻 mozilla MDN

💻 쿠키? 그거 먹는 건가요?

💻 TIL71. HTTP : 쿠키와 캐쉬 관련 Header

💻 쿠키와 세션 개념

💻 쿠키(Cookie)와 세션(Session)의 차이 (+캐시(Cache))

💻 XSS공격과 쿠키

💻 쿠키에 대하여

💻 세션 동작 원리

💻 배포 후 쿠키, 세션 문제

💻 윈도우 10 NEXTJS localhost https 적용하기

댓글남기기