CSRF 보호
번역일: 2026년 6월 20일
CSRF 보호
소개
CSRF(Cross-Site Request Forgery)는 인증된 사용자의 권한을 도용해 의도치 않은 요청을 서버에 보내는 공격 기법입니다. Laravel은 이러한 CSRF 공격으로부터 애플리케이션을 쉽게 보호할 수 있는 기능을 기본으로 제공합니다.
취약점 동작 원리
CSRF가 낯설다면, 구체적인 예시를 통해 이해해 봅시다. 여러분의 애플리케이션에 인증된 사용자의 이메일 주소를 변경하는 POST /user/email 라우트가 있다고 가정합니다. 이 라우트는 폼 필드 email에 담긴 새 이메일 주소를 받아 처리합니다.
CSRF 보호가 없다면, 악의적인 외부 사이트가 다음과 같은 HTML 폼을 만들어 사용자 몰래 요청을 보낼 수 있습니다.
<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 요청 방어
Laravel은 애플리케이션이 관리하는 각 사용자 세션마다 CSRF 토큰을 자동으로 생성합니다. 이 토큰은 실제로 인증된 사용자가 요청을 보냈는지 확인하는 데 사용됩니다. 토큰은 세션에 저장되며 세션이 재생성될 때마다 갱신되므로, 외부 사이트에서는 이 값에 접근할 수 없습니다.
현재 세션의 CSRF 토큰은 Request 객체의 세션이나 csrf_token() 헬퍼 함수로 가져올 수 있습니다.
use Illuminate\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});POST, PUT, PATCH, DELETE 방식의 HTML 폼을 작성할 때는 반드시 숨겨진 _token 필드를 포함해야 합니다. CSRF 보호 미들웨어가 이 값을 검증합니다. Blade의 @csrf 디렉티브를 사용하면 간편하게 추가할 수 있습니다.
<form method="POST" action="/profile">
@csrf
<!-- 아래 코드와 동일합니다 -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>web 미들웨어 그룹에 기본으로 포함된 App\Http\Middleware\VerifyCsrfToken 미들웨어가 요청에 담긴 토큰과 세션에 저장된 토큰이 일치하는지 자동으로 검증합니다. 두 값이 일치하면 요청이 실제 인증된 사용자로부터 온 것임을 확인할 수 있습니다.
CSRF 토큰과 SPA
Laravel을 API 백엔드로 사용하는 SPA를 개발하고 있다면, 인증 및 CSRF 보호 방법에 대해 Laravel Sanctum 문서를 참고하세요.
특정 URI를 CSRF 보호에서 제외하기
경우에 따라 일부 라우트를 CSRF 검증 대상에서 제외해야 할 수 있습니다. 예를 들어 결제 서비스(Stripe, 토스페이먼츠 등)의 웹훅을 수신하는 라우트가 있다면, 외부 서비스는 CSRF 토큰을 알 수 없으므로 해당 라우트를 제외해야 합니다.
이런 라우트는 routes/web.php의 web 미들웨어 그룹 밖에 정의하는 것이 일반적입니다. 또는 VerifyCsrfToken 미들웨어의 $except 속성에 URI를 추가하는 방법도 있습니다.
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* CSRF 검증에서 제외할 URI 목록
*
* @var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}NOTE
테스트 실행 중에는 편의를 위해 모든 라우트에서 CSRF 미들웨어가 자동으로 비활성화됩니다.
X-CSRF-TOKEN
VerifyCsrfToken 미들웨어는 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 프레임워크와 라이브러리는 동일 출처(same-origin) 요청 시 이 쿠키 값을 자동으로 X-XSRF-TOKEN 헤더에 포함시켜 줍니다.
NOTE
resources/js/bootstrap.js 파일에는 Axios HTTP 라이브러리가 기본으로 포함되어 있으며, Axios는 X-XSRF-TOKEN 헤더를 자동으로 전송합니다.