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.php의 web 미들웨어 그룹 밖에 정의하는 것이 일반적입니다. 또는 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 보호가 자연스럽게 동작하는 이유가 바로 이 때문입니다.