-
[Spring Security] CSRF 에서 출발한 여행기 (CSRF 토큰만들기)(Feat. Spring & React)Back End/Spring Security 2021. 12. 3. 19:55
이 글은 CSRF 공격방식에 대한 글을 읽고,
이에 대한 방어 수단으로 쿠키를 만든다길래 직접 해보고 싶어
이리저리 삽질한 비전공자의 여행일기입니다.
(+ 학생 때 시도한 뻘짓 기록 일기입니다. CSRF 적용관련 정보가 필요하시다면 최신포스팅을 읽어주세요!)
# 한국인은 결론 먼저 ..!
결론부터 말씀드리자면, Spring Security 는 default로 헤더에 Cache-Control 를 추가하고, no-cashe, no-store 등의 옵션을 추가 합니다.
이는 브라우저에게 '응 쿠키 못써' 라고 알려주는 역할을 합니다.
그러니까 쿠키를 사용하려면 Cache-Control 라는 헤더를 없애주어야 합니다.
그리고, 쿠키를 쓰는 방식은 보안에 취약하여 이제는 쿠키 없이 보안설정을 한다고 합니다. 크롬이 그러한 노력을 많이 하는 것 같습니다.
Spring Security 또한 이러한 추세에 맞춰 default 로 Cache-Control 에 옵션을 추가하지 않았나 추측합니다.
따라서 이제 Spring에서는 csrf 공격에 대비하기 위하여 csrf 토큰을 만들필요가 없습니다.
즉, 저처럼 머리아프게 고민하지마시고 WebSecurityConfig 를 설정하실 때 csrf().disable() 을 해주시면 됩니다.
* 여기부터는 필자의 삽질에 대한 울분으로 얼룩진 일기장입니다.
* 추후 다시 csrf 쿠키에 대한 구현에 도전할 때, 미리 겪었던 과정을 기억하기 위해 기록합니다.
# 필자가 직접 구현해보고 싶었던 이유
CSRF토큰을 가지고 서버가 클라이언트를 식별한다는데,
그렇다면 어느시기에 CSRF토큰을 클라이언트에게 정확히 던져주는지 알고 싶었다.
(GET 요청은 안막는다니까, GET을 할때 클라이언트에게 전달 해주는건가?
만약 그렇다면 사용자가 React 앱에 처음 접근하면 무조건 GET을 서버에 날려 토큰을 받아야하나?
이게 맞나? 라고 어림짐작만 할 수 있을뿐,
똑똑한 선배개발자들이 던져주는 시기를 언제로 정했을지 너무 궁금하고 알고싶었다.)
그래서 Spring Security 를 사용하여 CSRF를 해결하면,
그 안의 로직을 염탐할 수 있을 것 같아 Spring Security 를 사용하기로 결심하였다. (불행의시작...)
# CSRF 키워드로 구글링한 결과 게시물의 성향 🤬
필자는 프론트는 React 로 구성하였으며, 백엔드는 Spring으로 구성하였기에 포트번호가 다르다. (CORS 케이스에 해당)
CSRF 토큰의 개념과 적용방식은 알겠는데, 이왕이면 Spring Security 를 사용하여 다루고 싶었다.
(Spring Security 없이 구현하는게 훨씬 쉽고 빠르다.)
# 첫번째 유형 - 제일 많은 게시물 유형 (... 당신들 개발자가 되긴할것인가?)
" 아 csrf 때문에 안됬구나 ㅎ disable() 해야지! "
.... csrf에 대한 설명과 그에 대한 방어법의 필요성에 대한 이야기를 주구장창하면서, 아무도 disable()하는 이유를 알려주지 않는다.
... 🤕
# 두번째 유형 (나는 그래도 csrf 설정했지롱 이거봐!)
리액트 없이, Spring MVC JSP 페이지에 csrf 토큰을 만들어 Ajax를 적용하는 유형 (Link)
(필자는 React로 프론트를 SPA로 구현했기에 해당되지않는다.)
** (그리고 SPA에서는 form 태그를 지양한다.) **
# 세번째 유형 (영어로 검색했을 때 보이는 유형) - 심지어 게시글은 양이 조금있는데 코드는 다 똑같다. (복붙러들..)
"Spring에서 이렇게하고, React에서 이렇게 하면 됨!" (Link)
=> 그대로 따라하면 안된다. Spring Security 의 버전변경으로 구현 방식이 조금 달라짐
세번째 유형까지 대면 한 후, 나는 Spring Secuirty 의 코드들을 하나 씩 뜯어보기 시작했다.
그리고 세번째 유형의 로직에 맞게 코드를 만들었다. 그런데 안됬다.. (결국 뻘짓)
# 왜 안될까? 의 무한 루프 (403, 403, 403....)
작성한 코드를 계속 조금씩 수정하면서 원인을 찾기 위해 계속 테스트를 진행하였다.
심지어 Spring Security 단에서 막아버리고 403 을 던져버려서 🤬
로깅을 하기위해 Filter단에 출력문을 왕창넣어뒀으나 실행조차도 안된다.
(나 같은 코린이들은 이유조차 알 권리가 없는 것인가 ... 😢)
그러다, StackOverFlow 에서 결론에 있는 빛의 사나이 Arun 님을 영접했고 로깅도 안되는 필자가 감히 원인을 유추해볼 수 있게 되었다.
(그러니까 payload 라는 곳에 담기고, Spring Security 가 no cookie 정책으로 디자인 되어있어서 csrfRepository가 안먹힐 수도 있다 이거지?)
# 그렇다 나는 기억한다 Cache-Control ...
그렇다 수많은 삽질 도중, 403을 날려버리는 서버에서 헤더로 무엇을 나에게 줬었는지 나는 기억한다.
분명 Spring Security 를 적용하기전에는 없었던 헤더라 예의 주시했었던 헤더다.
# 좋아 당장 저놈을 없애고 내가 작성한 코드를 실행해보자!
진짜 킹론상 완벽하다. 저놈이 없으면 GET 메서드로 CSRF쿠키를 얻을 수 있지 않을까!!! (응 아니야)
..... GET과 POST의 Response에 담겨오는 XSRF-TOKEN 의 값이 다르다..... 일단 이건 나중에 해결한다 쳐도
Application->Cookies 에 쿠키가 세팅이 안된다. (이게 가장 큰 문제)
React 에서 저장하는 로직을 만들어야하나? 싶어 response 에 접근하려해도 해당 헤더에 접근할 수가 없다.
분명 개발자도구 Network 에서 확인한 헤더에는 Set-Cookie 라는 놈이 있는데,
React 에서 console.log 로 찍은 response 에는 헤더가 없다..........
(정말 파면 팔수록 모르는 것 투성이... 네트워크 과목 수강 절실해 🥺)
# 예측, 또 예측 (비전공자의 비애)
'Set-Cookie' 라는 헤더에 접근하여 토큰에 접근하고 싶었는데, 헤더에 없네...? 그렇다면 react 단에서 저장하는게 아니라 브라우저 단에서 알아서 Set-Cookie 를 만나면 저장해주는걸까? 그래서 'header setcookie not working' 라는 키워드로 구글링을 하였다.
StackOverFlow 에서도, 한국인 블로그 포스팅에서도 갑자기 https 에 관한 이야기를 하고있다.
또 다른 한국인 블로거 분은 브라우저의 종류(크롬과 파이어폭스)에 대한 이야기를 하고 계신다.
그렇다. 이건 단순한 코딩문제가 아니였다. 네트워크에 대한 베이스를 쌓아 올리지 않고선 해결하기 위해 시간낭비가 엄청날 것 같다.
일단 진행하고 있는 프로젝트 완성부터 하고 찬찬히 네트워크에 대한 공부를 해야겠다 싶어 csrf().disable() 로 설정하기로 했다.
나중에 또 보자 ^ㅡ^ 망할 쿠키녀석들....
# Reference
1. Spring Security 공식 Doc (Default Header에 관한글)
2. 컨텐츠 협상과 Vary 헤더
3. Vary 헤더 detail
728x90'Back End > Spring Security' 카테고리의 다른 글