본문 바로가기

CSRF 보호

업데이트됨

번역일: 2026년 6월 20일

이 페이지는 원문이 업데이트되어 번역이 갱신되었습니다.

원문 수정
2026년 6월 20일
번역 갱신
2026년 6월 20일

CSRF 보호

소개

CSRF(Cross-Site Request Forgery)는 인증된 사용자를 대신해 의도하지 않은 요청을 서버에 보내는 공격 기법입니다. Laravel은 이 공격을 쉽고 효과적으로 방어할 수 있는 기능을 기본으로 제공합니다.

취약점 예시

CSRF가 낯선 분들을 위해 간단한 시나리오로 설명합니다.

여러분의 애플리케이션에 /user/email 라우트가 있고, 이 라우트는 POST 요청을 받아 로그인된 사용자의 이메일 주소를 변경한다고 가정합니다.

CSRF 보호가 없다면, 악성 사이트는 아래와 같은 폼을 만들어 사용자 몰래 자동으로 제출할 수 있습니다:

<form action="https://your-application.com/user/email" method="POST"> <input type="email" value="malicious-email@example.com"> </form> <script> document.forms[0].submit(); </script>

페이지가 로드되는 순간 폼이 자동으로 제출되므로, 공격자는 여러분의 사용자가 해당 악성 페이지를 방문하도록 유도하기만 하면 됩니다. 사용자는 아무 행동도 하지 않았지만, 이메일 주소가 변경되는 피해를 입게 됩니다.

이 취약점을 막으려면 POST, PUT, PATCH, DELETE 요청마다 악성 사이트가 알 수 없는 비밀 세션 값을 함께 검증해야 합니다.

CSRF 요청 방어

web 미들웨어 그룹에 기본으로 포함된 Illuminate\Foundation\Http\Middleware\PreventRequestForgery 미들웨어는 두 단계 방식으로 CSRF 공격을 방어합니다.

1단계 — Origin 헤더 검증: 미들웨어는 먼저 브라우저가 자동으로 전송하는 Sec-Fetch-Site 헤더를 확인합니다. 요청이 동일한 Origin에서 왔다고 판단되면, 토큰 검증 없이 즉시 요청을 허용합니다.

2단계 — CSRF 토큰 검증: Sec-Fetch-Site 헤더가 없거나(오래된 브라우저), HTTPS가 아닌 환경이라면 전통적인 CSRF 토큰 방식으로 대체(fallback)하여 검증합니다.

Laravel은 각 사용자 세션마다 고유한 CSRF 토큰을 자동으로 생성합니다. 이 토큰은 세션에 저장되며 세션이 재생성될 때마다 바뀌기 때문에, 외부 악성 사이트에서는 접근 자체가 불가능합니다.

현재 세션의 CSRF 토큰은 다음과 같이 가져올 수 있습니다:

use Illuminate\Http\Request; Route::get('/token', function (Request $request) { $token = $request->session()->token(); $token = csrf_token(); // ... });

HTML 폼에서 POST, PUT, PATCH, DELETE 요청을 보낼 때는 반드시 숨겨진 _token 필드를 포함해야 합니다. Blade의 @csrf 디렉티브를 사용하면 간편하게 추가할 수 있습니다:

<form method="POST" action="/profile"> @csrf <!-- 아래 코드와 동일합니다 --> <input type="hidden" name="_token" value="{{ csrf_token() }}" /> </form>

CSRF 토큰과 SPA

Laravel을 API 백엔드로 사용하는 SPA를 개발하고 있다면, Laravel Sanctum 문서를 참고하세요. Sanctum은 SPA 환경에서의 인증 및 CSRF 방어를 체계적으로 지원합니다.

Origin 검증

앞서 설명한 것처럼, 미들웨어는 먼저 Sec-Fetch-Site 헤더를 확인하여 요청이 동일 Origin에서 왔는지 판단합니다. 기본 동작은 Origin 검증에 실패하면 CSRF 토큰 검증으로 대체하는 것입니다.

Origin 검증만으로 충분하고 CSRF 토큰 대체 방식을 완전히 비활성화하고 싶다면, bootstrap/app.php 파일에서 다음과 같이 설정할 수 있습니다:

->withMiddleware(function (Middleware $middleware): void { $middleware->preventRequestForgery(originOnly: true); })

이 모드를 사용하면 Origin 검증에 실패한 요청은 기존의 419 대신 403 HTTP 응답을 받게 됩니다.

WARNING

Sec-Fetch-Site 헤더는 브라우저가 HTTPS 연결에서만 전송합니다. 애플리케이션이 HTTPS로 서비스되지 않는 경우 Origin 검증을 사용할 수 없으며, 미들웨어는 자동으로 CSRF 토큰 검증 방식으로 대체됩니다.

서브도메인 간 요청을 허용해야 하는 경우(예: dashboard.example.com에서 example.com으로의 요청), 동일 사이트(same-site) 요청도 허용하도록 설정할 수 있습니다:

->withMiddleware(function (Middleware $middleware): void { $middleware->preventRequestForgery(allowSameSite: true); })

특정 URI를 CSRF 보호에서 제외

일부 URI는 CSRF 보호에서 제외해야 할 수 있습니다. 대표적인 예가 결제 서비스의 웹훅입니다. 예를 들어 PG사나 Stripe 같은 외부 서비스의 웹훅 엔드포인트는 서버 간 통신이므로 CSRF 토큰을 포함할 수 없습니다.

이런 라우트는 routes/web.phpweb 미들웨어 그룹 밖에 정의하는 것이 일반적입니다. 또는 bootstrap/app.php에서 preventRequestForgery 메서드를 통해 특정 URI를 명시적으로 제외할 수도 있습니다:

->withMiddleware(function (Middleware $middleware): void { $middleware->preventRequestForgery(except: [ 'stripe/*', 'http://example.com/foo/bar', 'http://example.com/foo/*', ]); })

NOTE

테스트 실행 시에는 편의를 위해 모든 라우트에서 CSRF 미들웨어가 자동으로 비활성화됩니다.

X-CSRF-TOKEN

PreventRequestForgery 미들웨어는 POST 파라미터로 전달된 _token 외에도, X-CSRF-TOKEN 요청 헤더도 함께 확인합니다.

HTML <meta> 태그에 토큰을 저장해 두면:

<meta name="csrf-token" content="{{ csrf_token() }}">

jQuery 등의 라이브러리를 이용해 모든 AJAX 요청에 자동으로 헤더를 추가할 수 있습니다. 레거시 JavaScript 환경에서 AJAX 기반 애플리케이션을 보호하는 간편한 방법입니다:

$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });

X-XSRF-TOKEN

Laravel은 현재 CSRF 토큰을 암호화된 XSRF-TOKEN 쿠키에 담아 모든 응답에 함께 전송합니다. 이 쿠키 값을 읽어 X-XSRF-TOKEN 요청 헤더에 설정하면 CSRF 검증을 통과할 수 있습니다.

이 쿠키는 주로 개발 편의를 위한 것으로, Angular나 Axios 같은 주요 JavaScript 프레임워크 및 라이브러리가 동일 Origin 요청 시 이 쿠키 값을 자동으로 X-XSRF-TOKEN 헤더에 포함시켜 줍니다. 별도 설정 없이도 CSRF 보호가 자연스럽게 동작하는 이유가 바로 이 때문입니다.

이 문서는 Laravel 공식 문서(MIT)를 한국 개발자를 위해 번역·재구성한 것입니다.

번역일: 2026년 6월 20일