1. https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
아래 글은 위 사이트들의 글을 읽고 이해한 바로 작성되었으며, 사실과 다를 수 있습니다.
사실과 다른 부분이 있으면 댓글로 남겨주시기 바랍니다.
사실 쿠키에 대한 글인데, 쿠키를 크로스 오리진 간에 사용하는 경우가 많기 때문에 CORS시리즈로 넣었다.
쿠키
쿠키는 서버가 사용자의 브라우저에 전송하는 데이터조각이다.
브라우저는 이걸 저장했다가, 동일한 서버에 재요청시에 저장된 데이터를 함께 전송한다.
이렇게 하면 브라우저가 우리의 인증정보가 담긴 쿠키를 저장해서, 로그인이 유지되는 원리인 것이다.
자 그럼 이 그림을 자세히 보자.
①에서 요청을 하면 서버가 ②에서 응답을 한다.
이때 사용되는 것이 HTTP의 Set-Cookie헤더이다.
서버의 HTTP응답을 보자면...
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: 쿠키데이터
이렇게 브라우저가 응답을 받으면, 브라우저는 브라우저 공간에 Set-Cookie를 해준다.
이걸 개발자도구 > 어플리케이션 > 쿠키 탭에서 확인할 수 있다.
이렇게 설정된 쿠키가 이후에 행해지는 요청시에 자동으로 담겨서 서버로 가게된다.
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie:쿠키데이터
요청헤더를 네트워크 탭에서도 확인할 수 있다.
CORS와 쿠키
1편에서 살펴봤듯이 CORS는 다른 오리진간 리소스 공유를 위해서 사용되는 규칙이다.
그런데 쿠키도 하나의 리소스이다. 이건 그냥 CORS 규칙을 만족하면 공유가 되는 것일까?
답은 아니다.
사실은 조금 더 복잡하다.
쿠키를 사용하다보면 다양한 보안 문제들이 발생할 수 있고, 이걸 해결하기위해 우리의 슨배님들이 골머리를 싸매서 아래와 같은 옵션들을 만들어냈다. 먼저 문제들부터 살펴보자.
쿠키를 사용하면서 발생할 수 있는 문제들
1. 사용자에게 JS 스크립트를 어떤식으로든 실행시키게 해서 document.cookie를 통해 사용자의 쿠키를 직접적으로 얻어낸다. -> 계정이 탈취당한다.
2. 네트워크 패킷을 스니핑해서(뜯어내서) header에서 쿠키를 꺼내온다. -> 계정이 탈취당한다.
3. CSRF공격 -> 계정에 나도 모르는 일들이 일어날 수 있다. (광고 글쓰기 부터 비밀번호 변경까지)
CSRF가 뭔데
CSRF는 Cross Site Request Forgery
해석하면 교차 사이트간 요청 위조이다.
이걸 이해하려면 쿠키에 대한 이해가 필수적이다.
상황을 통해 CSRF가 어떻게 동작하는지 이해해보자.
인스타그램은 가정이다. 인스타그램이 실제로 쿠키-세션 방식으로 로그인을 하는지는 알 수 없다.
1. 인스타그램에 로그인했고, 자동로그인을 체크했다. (자동 로그인을 해야 쿠키 수명이 길어지므로)
2. 브라우저는 인스타그램 서버에 인증정보를 전송하고, 인스타그램 서버는 Set-Cookie헤더를 통해서 사용자의 세션정보를 Cookie로 등록했다.
3. 사용자가가 어쩌다가 특정 사이트에 접속했는데, 해당 사이트에는 https://instagram.com/changePassword에 비밀번호를 1234로바꾸는 요청을 보내는 코드가 포함되어있었다.
3. 사용자의 브라우저가 인스타그램 서버에 비밀번호 변경 요청을 보낸다.
4. 원래라면 쿠키가 없으므로 수행되지 않아야하지만, 자동로그인을 설정해두었으므로 쿠키가 전송되고 계정주인도 모르게 비밀번호가 바뀌어버린다.
나는 요청을 보낸적 없는데, 내가 요청을 보냈다(?)
요청이 내가 보낸것마냥 위조된 것이다. 이게 바로 CSRF다.
교차 사이트(인스타그램 서버와 특정 사이트)간 요청 위조(내가 보낸거마냥)인 것이다.
문제상황별로 우리 슨배님들이 만들어낸 옵션들을 살펴보자.
1. 사용자에게 JS 스크립트를 어떤식으로든 실행시키게 해서 document.cookie를 통해 사용자의 쿠키를 직접적으로 얻어낸다
이 방식을 막아내기 위해서는 두 가지 방법정도가 떠오를 것이다.
1. js 스크립트를 실행못하게한다.
2. js로 쿠키에 접근을 못하게 한다.
1번을 하게되면 사이트가 그냥 실행이 안될테니까... 우리 슨배님들은 2번을 택했다.
httpOnly
httpOnly는 http통신을 통해서만 유일하게 쿠키에 접근할 수 있다는 것이다.
js에서 접근을 못하니 js코드로는 이제 쿠키를 뜯어갈 수가 없다...!
2. 네트워크 패킷을 스니핑해서(뜯어내서) header에서 쿠키를 꺼내온다.
네트워크 패킷을 뜯어서 내용을 볼 수 있는 것은 암호화 되지 않은 통신이라면 무조건 겪는 문제다.
문제는 이걸 알고 있는 사람은 IT관련 종사자뿐이라는 것...
모르는 사람이 사이트에 가입하고, 모르는체로 쿠키를 탈취당하면 정말 큰일이다.
이걸 해결하려면? 하나뿐이다. https가 아닌 http에서 쿠키를 못쓰게 하면된다.
Secure
secure 옵션이 부여된 쿠키는 HTTPS프로토콜을 사용해 암호화된 요청일 때만 요청 헤더에 담겨서 서버에게 날아간다.
HTTP인경우 요청에 쿠키를 담을 수 없게 하는 것이다.
3. CSRF공격
위의 예시를 보듯, 이 문제는 다른 사이트에서 해당 사이트로 쿠키 요청을 보냈기때문에 발생하는 문제라는걸 알 수 있다.
그럼 A사이트에서 B사이트로 쿠키 요청을 못보내게 하면 되겠네?
SameSite
sameSite는 동일한 사이트에서 보낸 요청인지를 검증해서 CSRF공격인지 아닌지를 판단할 수 있게 만들어준다.
SameSite 쿠키의 정책으로 None, Lax, Strict 세 가지 종류를 선택할 수 있고, 각각 동작하는 방식이 다르다.
1. None: 그냥 쿠키와 동작하는 방식이 같다. None으로 설정된 쿠키의 경우 크로스 사이트 요청의 경우에도 항상 전송된다. 이렇게 사용하려면, secure옵션을 설정해서 https통신으로 설정해야한다.
2. Strict: Strict로 설정된 쿠키는 크로스 사이트 요청에는 항상 전송되지 않는다. 같은 도메인인 경우에만 전송이된다.
3. Lax: Strict에 비해 상대적으로 느슨한 정책으로, Lax로 설정된 경우, 대체로 서드 파티 쿠키(CORS인경우)는 전송되지 않지만, 몇 가지 예외적인 요청에는 전송된다.
같은 웹 사이트일 때는 (당연히) 전송되고, 이 외에는 웹 페이지 이동과, "안전한" HTTP 메서드 요청의 경우 전송된다.
이 안전한 HTTP메서드 요청의 경우는 이전 게시글을 참고해주면된다.
위 세가지 문제들에 대해 쿠키 옵션으로
httpOnly, Secure, SameSite를 적절하게 지정하여 해결할 수 있는 것이다!
따라서 만약 다른 오리진간(사이트) 통신에 쿠키를 사용해서 인증시스템을 만드려면, SameSite는 None으로, Secure를 true로 해서 서버를 https로 올려줄 필요가 있다.
Ajax통신(fetch, axios)과 쿠키
Ajax통신을 통해 쿠키를 전송하는 경우, 그냥 요청과는 조금 다를 수 있다.
fetch의 예시를 들자면, credentials 옵션을 'include'로 설정해주어야한다.
다른 옵션으로는 'same-origin'(같은 출처일때), 'omit' (쿠키전송을 하지않음)
다만, 이때, CORS설정시 서버에서 설정한 Access-Control-Allow-Origin이 *(와일드카드)인경우, 쿠키가 제대로 전달되지 않는다.
https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Using_Fetch