CSRF ?? (Cross Site Request Forgery)
CSRF 는 Cross Site Request Forgery 의 약자로 크로스 사이트 요청 위조라고 불립니다. 악의적으로 사이트를 요청을 위조해서 서버에 위해를 가하는 웹 사이트 공격 방법입니다. OWASP 에서 선정한 웹 어플리케이션 보안 취약점 10가지 중 하나이다. CSRF 를 이해하기 쉽게 그림으로 살펴보겠습니다.

- 악의적인 의도를 품은 Attacker가 Application 사용자 대상으로 XSS(크로스 사이트 스크립팅), 피싱 사이트로 공격을 실행합니다.
- 권한이 있는 User는 Attacker의 공격에 따라 어플리케이션에 주로 데이터 변경과 관련된 (POST, PUT, DELETE Method) 요청을 하게 합니다.
- 이 요청을 통해 Attacker는 어플리케이션에서 User Cookie, Session 등 정보를 탈취하거나 어플리케이션에 치명적인 손상을 줍니다.
CSRF Protection
이런 공격 패턴을 지니고 있는 CSRF 공격에는 어떤 방어 방법들이 있는 간략하게 알아보겠습니다.
1. HTTP referer Checking
제일 간단한 방법입니다. HTTP Header 중 이전 도메인에 대한 정보를 가지고 있는 HTTP referer 를 통해 요청 도메인에 대한 분석을 통해 접근을 제한하는 방법입니다. 하지만 같은 도메인 내에서 XSS 공격을 하는 경우 취약점이 들어나며 referer 변조를 통해 취약점이 들어나기도 합니다.
2. Synchronizer token pattern
<input type="hidden" name="csrfmiddlewaretoken" value="KbyUmhTLMpYj7CD2di7JKP1P3qmLlkPt" />
위의 소스 코드와 같이 form 태그 아래 Token를 숨겨 이 토큰을 통해 서버에 있는 토큰과 비교하여 CSRF 공격 여부를 판단하는 방법입니다. token 이 일치하지 않는 경우 요청을 무시합니다.
3. Cookie-to-header token
Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/Ti
Token를 사용하는 점에서 위의 Synchronizer token 방어 방법과 비슷합니다만 Cookie header 에 csrf token를 같이 보내는 방법입니다. 사실 토큰 정보를 탈취하여 Cookie를 수정하고 보내는 경우에는 위험하다고 생각할 수 있으나 web browser 의 Same Origin 원칙에(동일 출처 정책) 의존하여 이를 방지하는 취약점 보호 방법입니다.
4. Double Submit Cookie
Cookie-to-header 방식과 비슷하지만 차이가 있습니다. CSRF Token를 쿠키에 Set하고 form에 hidden field를 보냅니다. 이 form 이 전송될때 사이트는 Cookie 에 있는 토큰값고 매칭되는지 체크합니다. 이 방식 또한 Same Origin 원칙에 의해 쿠키 정보에 접근하는 것을 막을 수 있습니다. Synchronizer pattern 과 다르게 서버에 토큰 정보를 저장하지 않아도 된다는 장점이있습니다.
Laravel CSRF Protection
Laravel 프레임워크는 다른 프레임워크(Ruby on rails 등) 처럼 자동으로 CSRF Protection 를 제공합니다. CSRF Protection 방법 중 어떤 것으로 구현하고 있는지 생각하면서 Laravel CSRF Protection 에 대해 익혀보겠습니다.
FORM 태그 @CSRF 사용
<form method="POST" action="/profile"> @csrf // = <input type='hidden' name="_token" value="AWE4636AETAetcae324"> ... </form>
form 태그 안에 @csrf 입력하면 Blade 라는 템플릿 엔진을 통해 hidden field 와 토큰을 생성합니다. crsf token 과 더불어 요청이 들어오면 라라벨은 web group middleware 인 VerifyCsrfToken 를 통해 세션에서 토큰을 비교하여 취약점을 보호합니다.
X-CSRF-TOKEN
<meta name="csrf-token" content="{{ csrf_token() }}">
Javascript 를 이용해 비동기로 요청하는 경우에는 위처럼 form 태그안에 @csrf 를 이용하는 대신 다른 방법으로 대신합니다. meta 태그에 csrf_token를 추가하고 비동기 요청을 진행할 시 라라벨에 내장되어 있는 axios 라이브러리를 사용할 경우에는 request header 에 ‘X-CSRF-TOKEN’ 에 토큰 정보를 같이 전송하게 됩니다. bootstrap.js 파일을 살펴보면 이해가 쉽습니다.
// bootstrap.js // ... // // meta tag의 정보를 읽고 default 로 헤더에 X-CRSF-TOKEN 를 포함시킨다. let token = document.head.querySelector('meta[name="csrf-token"]'); if (token) { window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; } else { console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token'); } // ... //
X-XSRF-TOKEN
VertifyCsrfToken 미들웨어는 Response cookie에 X-XSRF-TOKEN 라는 이름으로 토큰 정보를 저장합니다. ($addHttpCookie 값으로 쿠키 저장 여부 결정할 수 있다.) 사용자는 이 쿠키의 토큰을 가지고 편리하게 요청을 할 수 있다. VeritfyCsrfToken 미들웨어 소스 코드를 열어보면 Laravel CSRF token 이해에 도움이 됩니다.
<?php namespace Illuminate\Foundation\Http\Middleware; class VerifyCsrfToken { // crsf token protection 이 필요없는 url를 정의할 수 있다. protected $except = []; protected $addHttpCookie = true; // tokenMatch 에서 볼 수 있듯이 session token 과 요청 토큰을 비교한다. // 이를 통해 Synchronizer token 방식이란 것을 알 수 있다. protected function tokensMatch($request) { $token = $this->getTokenFromRequest($request); return is_string($request->session()->token()) && is_string($token) && hash_equals($request->session()->token(), $token); } // session 정보에 접근하여 response시 Cookie X-XSRF-TOKEN 로 토큰을 저장한다. protected function addCookieToResponse($request, $response) { $config = config('session'); $response->headers->setCookie( new Cookie( 'XSRF-TOKEN', $request->session()->token(), $this->availableAt(60 * $config['lifetime']), $config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null ) ); return $response; } }
주의깊게 볼만한 Method는 tokenMatch() 입니다. meta tag 또는 hidden field 또는 X-XSRF-TOKEN 의 Request 로 넘어온 token 값을 Session 토큰과 비교하여 취약점을 보완하고 있습니다. Laravel은 위에서 본 보호 방법 중 Synchronizer Pattern 를 사용하여 CSRF Protection 를 구현하고 있다는 것을 알 수 있습니다.
TL;DR
- CSRF (Cross Site Request Forgery) 은 악의적으로 사이트 요청을 위조하여 웹 어플리케이션에 치명적인 손상이나 유저에게 피해를 입히는 공격입니다.
- Laravel 에서는 CRSF 보호를 위해 token 방식을 이용하며 이 토큰들을 자동으로 생성하고 요청에 포함시켜 사용합니다.