메인 콘텐츠로 건너뛰기

소개

CSRF(Cross-Site Request Forgery)는 로그인된 사용자를 사칭하여 의도하지 않은 요청을 보내게 하는 공격입니다. 예를 들어, 여러분의 애플리케이션에 POST /user/email이 있어 이메일 주소 변경을 받고 있다고 가정해 봅시다. 공격자가 다른 사이트에서 이 URL로 자동 전송하는 폼을 심어 두면, 사용자가 알아차리지 못하는 사이에 이메일 주소가 변경될 가능성이 있습니다. Laravel 13에서는 web 미들웨어 그룹에 포함된 구조 덕분에 CSRF 보호가 기본적으로 활성화되어 있습니다.

CSRF 공격 방지

Laravel의 PreventRequestForgery 미들웨어는 다음 2단계로 CSRF를 방지합니다.
  1. Origin 검증(Sec-Fetch-Site 헤더)
  2. 토큰 검증(세션 단위의 CSRF 토큰)
먼저 Origin을 확인하고, 판정할 수 없거나 실패한 경우에는 토큰 검증으로 폴백합니다.

Origin 검증

Laravel은 먼저 Sec-Fetch-Site를 확인하여 동일 Origin에서의 요청인지 판정합니다. 이는 HTTPS 환경에서 특히 효과적입니다. Origin 검증을 통과하면 그 시점에서 요청은 허용됩니다. 통과하지 않는 경우에는 기존과 동일하게 CSRF 토큰 검증이 실행됩니다.
Sec-Fetch-Site는 HTTPS 연결에서 사용되는 것이 전제입니다. HTTP 환경에서는 Origin 검증이 동작하지 않으며, 토큰 검증이 주요 방어 수단이 됩니다.

Origin-only 모드

토큰 검증으로의 폴백을 무효화하고, Origin 검증만으로 판정할 수도 있습니다.
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(originOnly: true);
    });
Origin-only 모드에서는 Origin 검증에 실패한 요청은 419가 아닌 403을 반환합니다. 서브도메인 간 요청 등에서 same-site를 허용하고 싶은 경우에는 allowSameSite를 설정합니다.
$middleware->preventRequestForgery(allowSameSite: true);

토큰 검증

Laravel은 세션마다 CSRF 토큰을 생성합니다. csrf_token() 또는 세션에서 가져올 수 있습니다.
use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
    $tokenFromSession = $request->session()->token();
    $tokenFromHelper = csrf_token();
});
web 라우트에서 POST·PUT·PATCH·DELETE 폼을 만드는 경우에는 반드시 @csrf를 포함시켜 주세요.
<form method="POST" action="/profile">
    @csrf

    <!-- 동등한 hidden input -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>

URI 제외

Stripe Webhook처럼 외부 서비스에서 전송되는 요청에서는 특정 URI를 CSRF 보호에서 제외하는 경우가 있습니다.
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(except: [
            'stripe/*',
            'http://example.com/foo/bar',
            'http://example.com/foo/*',
        ]);
    });
가능하면 Webhook 라우트는 web 미들웨어 그룹 외부에 배치하고, 제외 설정은 최소한으로 유지해 주세요.

X-CSRF-TOKEN 헤더

Laravel은 폼의 _token뿐만 아니라 X-CSRF-TOKEN 헤더도 검증합니다. 먼저 토큰을 meta 태그에 출력합니다.
<meta name="csrf-token" content="{{ csrf_token() }}">
그 값을 AJAX 요청 헤더에 부여합니다.
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': document
            .querySelector('meta[name="csrf-token"]')
            .getAttribute('content'),
    },
});

X-XSRF-TOKEN 헤더

Laravel은 암호화된 XSRF-TOKEN 쿠키도 전송합니다. Axios나 Angular는 동일 Origin 요청 시 이 값을 X-XSRF-TOKEN 헤더에 자동으로 설정할 수 있습니다. 따라서 SPA나 AJAX 구현에서는 헤더 설정을 수동으로 작성하지 않아도 CSRF 대책이 활성화되는 경우가 있습니다.

SPA에서의 고려 사항

SPA에서 Laravel을 API 백엔드로 사용하는 경우, 먼저 CSRF 쿠키를 획득한 후 로그인 요청을 보냅니다.
await axios.get('/sanctum/csrf-cookie');
await axios.post('/login', {
    email: '[email protected]',
    password: 'password',
});
자세한 내용은 Sanctum을 참조해 주세요.
마지막 수정일 2026년 7월 13일