Browser based CSRF Mitigation? Yoooh!!!!!! Same-Site-Cookie!
Chrome 76 부터 Same-Site-Cookie 설정이 default로 enabled되었다는 것을 보았다. 사용자의 Privacy와 CSRF를 막기위해 개발되었다. Privacy는 쿠키를 통한 사용자 트래킹 (?)을 막을 수 있게 되었다는데 이 글에선 CSRF를 중점적으로 다룰 것이다.
우선 CSRF가 무엇인지 이해하고 넘어가야 한다. 하나의 예시를 보여주도록 하겠다.
악의적인 공격자의 웹페이지에 아래와 같은 코드가 있다고 생각해보자.
<html> <body onload=x.submit()> <form id=x method=post action=http://vic.tim/delete/3> </form> </body> </html>
vic.tim 사이트에 CSRF를 방어할 수 있는 로직이 없다면 피해자는 자신도 모르게 3번 게시글을 삭제할 것이다. 이를 방어하기 위해 지금껏 시큐리티 미들웨어나 프레임워크들이 했던 방식은 크게 2가지이다.
- CSRF Token 검증
- Referer 헤더 검증
위 방법은 CSRF 공격을 막는데 효과적이지만 모든 입력받는 컨트롤러에서 확인을 해줘야 하기 때문에 효율적이지는 못하다.
이제 세션쿠키에 헤더를 추가하는 것만으로도 CSRF를 방어할 수 있는 시대가 왔다. 간단한 예제를 통해 알아보자.
Cookie를 설정할 때 CookieName=foobar; Domain=..; Expires=…; 쿠키 key=value 말고 추가적으로 Domain, Expires와 같은 설정을 추가할 수 있다. 이곳에 Same-Site 라는 설정 값이 추가된 것이다.
SameSite의 값은 Strict, Lax, None을 지원한다. 각각의 기능을 설명해보면,
Strict – 사용자가 직접 주소창에 입력하는 top-level navigation을 제외한 모든 요청에 Cookie가 포함되지 않는다.
<?php header("Set-Cookie: foo=bar; SameSite=Strict;"); header("Set-Cookie : bob=alice; SameSite=Lax;"); header("Set-Cookie : juno=im;"); echo '<pre>'; var_dump($_SERVER['HTTP_COOKIE']); echo '</pre>';
<a href="http://app.imjuno.com/q/">go</a> <br> <form method="post" action="http://app.imjuno.com/q/"> <button type=submit>go</button> </form>
Lax와 Normal 쿠키만 전송되는 것을 확인할 수 있다.
FORM을 이용한 POST 요청은 Normal 쿠키만 전송된다. 아래는 요청 타입별로 어떤 Same-Site 쿠키가 전송되는지에 대한 정리표이다. 개발할 때 참고하면 좋을 것 같다.
MDN에는 아직 Experimental 기능이라고 나오지만 현재 모든 브라우저에서 지원하고 있다. Chrome이 가장 먼저 적용한줄 알았는데 이미 다른 브라우저들은 적용했더라 -_-
구글에서 이미 서버사이드 프로그래밍에 자주 쓰이는 언어들에서 SameSite 쿠키를 사용하는 방법을 깃헙에 올려두었다.
사용자 세션에 SameSite=Strict를 사용할 경우 CSRF를 완벽하게 막을 순 있겠지만 a href를 통한 접속에 쿠키가 같이 보내지지 않기 때문에 유저 편의성을 해칠 수 있다. 예를 들면 이메일에서 링크를 눌러 접속한다던지….
마지막으로 어떠한 미티게이션을 도입하더라도 Silver Bullet은 없기 때문에 (XSS가 발생하면 무의미 해진다 ㅋㅋ) 서버의 코드를 잘 작성하는게 제일 중요하다는 것을 말하구 싶다~!~!
Reference
- https://web.dev/samesite-cookies-explained
- https://docs.adobe.com/content/help/ko-KR/target/using/implement-target/before-implement/privacy/google-chrome-samesite-cookie-policies.translate.html
SameSite 헤더를 포함했는데도 계속해서, 스크립트 에러는 난다. 왜 그런거지? 아직 이해가 되지 않네.. 해결이 안되니… 어떤 것 때문일까.. 하단 워닝을 지우려면??