여기서는 CSRF 공격이 무엇인지 알아보고, 이것을 예방하기 위해서 사용되는 CSRF Token에 대해서 정리를 할 것입니다.
CSRF는 웹사이트의 취약점을 이용하여 사용자가 의도하지 않는 행동을 유도하는 공격입니다.
Spring Security는 기본적으로 CSRF 보호를 제공하는데, 이는 Ajax 요청에도 적용됩니다. 따라서, 각 Ajax 요청에 CSRF 토큰을 포함해야 하며, 이를 누락하면 Spring Security는 해당 요청을 거부할 수 있습니다.
먼저 CSRF를 이해하기 위해서 이것을 이용한 공격에 대한 시나리오를 정리해 보겠습니다.
<img src="http://bank.com/transfer?to=attacker&amount=1000">
와 같은 코드가 있습니다. 이 코드는 뱅킹 웹 사이트에 대한 요청을 발생시킵니다.<img>
태그의 경우 ‘SOP 정책’ 에 영향을 받지 않고 요청을 보낼 수 있는 특성을 가지고 있고, 해당 코드를 실행하는 희생자의 브라우저는 저장된 쿠키를 자동적으로 담아서 보내게 됩니다.
서비스 서버에서는 해당 요청을 받아서 희생자가 원하지 않은 동작을 수행하게 될 것입니다.
위와 같은 CSRF 공격이 가능하기 때문에, JWT 만을 이용해서 사용자를 인증한다고 했을 경우에 매우 위험할 수 있습니다.
CSRF Token은 CSRF 공격을 방지하기 위해 사용되는 방법 중 하나입니다. 이 토큰은 서버에 의해 임의로 생성되며, 클라이언트 측에 저장됩니다.
여기서 중요한 점은, CSRF Token은 이전 토큰들과 다르게 단순 난수 문자열형식입니다. 케이스에 따라 암호화(예: HMAC)를 수행할 수 있지만 일반적으로는 그대로 사용합니다.
여기서는 일반적인 방식에 대해서 정리를 해보겠습니다.
자주 언급되는 방식은 다음 2가지 방식이 있습니다.
우선, 여기서는 JWT는 HTTP Only Cookie
에 담는 것을 가정하고 들어가겠습니다.
다음의 동작을 정리하자면 다음과 같습니다.
CSRF Token은 ‘hidden’ 상태로 되어있어서 UI 에서는 보이지 않습니다.
다만, 관리자 도구에서 소스 코드를 확인하면 value에 CSRF 토큰 값을 확인할 수 있습니다.
이 방식의 경우 웹 서버에서 리소스를 클라이언트가 요구했을 때 CSRF 토큰도 발급 받아서 제공해주면 되고, 클라이언트가 비멱등 요청을 실행하면 해당 토큰도 담겨서 보내지는 방식을 사용합니다.
이 방식을 사용할 경우 얻을 수 있은 장단점은 다음과 같습니다.
추가적으로 위의 로직이 성립하려면 SPA이 아닌 애플리케이션에서 동작을 해야 합니다.
이 방식의 경우에는 사용자가 로그인을 하고나서 JWT 를 발급받을 때 CSRF Token을 쿠키에 저장해두고, 사용자가 비멱등 요청을 보낼 때 JWT와 CSRF Token을 같이 보내서 두 토큰의 검증이 처리되면 요청을 처리하는 방식입니다.
기본적인 로직은 다음과 같습니다.
여기서, JWT Token을 먼저 검증하는 이유는 CSRF Token을 검증하기 위해서 유저의 고유한 정보(PK)가 필요한데 이것을 JWT로부터 받으려고 하는 것입니다. 또한, 로직 구성 자체만 봤을때도 JWT는 사용자를 검증하는 것이고 CSRF Token은 사용자의 요청을 한번 더 검증해주는 목적으로 사용하기 때문에 JWT 검증 후 CSRF Token을 검증하는 것이 맞다고 판단됩니다.
전통적인 웹 애플리케이션과는 다르게 SPA(Single Page Application)는 한 번 페이지를 로드한 후 새로운 페이지를 전체적으로 다시 로드하는 것 없이 동적으로 현재 페이지를 재작성하여 사용자와 상호작용합니다.
⚡️ SPA의 특징 ⚡️
- 동적으로 페이지 재작성: SPA는 필요한 부분만 업데이트하여 페이지 전체를 새로고침 X
- 빠른 페이지 전환: 모든 리소스가 초기에 로드, 추가 데이터는 API 호출을 통해 로드
- 브라우저 히스토리 관리: 대부분의 SPA 프레임워크와 라이브러리는 브라우저의 히스토리 관리하는 기능을 제공
- 서버 부하 감소: SPA는 초기 로드에 모든 리소스를 다운로드
여기서는 SPA에 대해서 자세히 다루지는 않겠습니다. 중요한 부분은 URI를 이동할 때 마다 필요한 페이지 리소스를 받아오는 것이 아니라, 기본적으로는 모든 리소스를 초기에 다운로드한다는 점입니다.
이 방식과 CSRF Token을 HTML에 담아서 보내는 방식을 사용한다면 페이지를 이동할 때 마다 CSRF Token을 받아와야 하는 네트워크 부담을 크게 줄일 수 있을 것입니다.
하지만, 다음과 같은 문제점이 있습니다.
최종적으로 정리를 하자면, 두 방식 중 어느 방식이 더 나을지는 애플리케이션 설계 정책에 맞게 선택할 수 있어야 합니다.