본문으로 건너뛰기
버전: 10.x
Laravel 10.x · 한국어 문서https://laravel.com/docs/10.x/csrf

CSRF Protection

Introduction​

크로스 사이트 요청 위조(Cross-site request forgery, CSRF)는 인증된 사용자를 가장하여 허가받지 않은 명령을 애플리케이션에 전달하는 악의적인 공격 방식 중 하나입니다. 다행히도 Laravel은 cross-site request forgery 공격으로부터 애플리케이션을 쉽게 보호할 수 있도록 기능을 제공합니다.

An Explanation of the Vulnerability​

CSRF가 무엇인지 익숙하지 않다면, 이 취약점이 실제로 어떻게 악용될 수 있는지 예시를 통해 설명드리겠습니다. 예를 들어, 여러분의 애플리케이션에 인증된 사용자의 이메일 주소를 변경하는 /user/email 경로가 있다고 가정하겠습니다. 이 경로는 일반적으로 POST 요청을 받고, email 입력 필드에 변경할 이메일이 전달되는 방식을 사용합니다.

만약 CSRF 보호가 없다면, 악의적인 웹사이트는 여러분의 애플리케이션의 /user/email 경로로 요청을 보내도록 아래와 같이 HTML 폼을 만들 수 있습니다. 이 폼은 공격자의 이메일을 자동으로 제출하게 됩니다:

<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="[email protected]">
</form>

<script>
document.forms[0].submit();
</script>

이처럼 악성 웹사이트가 페이지가 열리자마자 폼을 자동 제출하도록 하면, 여러분의 애플리케이션을 사용하는 사용자가 이 웹사이트에 접속하기만 하더라도 그 사용자의 이메일 주소가 공격자 의도대로 변경될 수 있습니다.

이러한 취약점을 방지하려면, 들어오는 모든 POST, PUT, PATCH, 또는 DELETE 요청마다 공격자가 접근할 수 없는 비밀 세션 값을 반드시 검사해야 합니다.

Preventing CSRF Requests​

Laravel은 애플리케이션에서 관리되는 모든 user session에 대해 자동으로 CSRF "토큰"을 생성합니다. 이 토큰은 인증된 사용자가 실제로 직접 요청을 보내고 있는지 검증하는 데 사용됩니다. 이 토큰은 사용자의 세션에 저장되며, 세션이 새로 생성될 때마다 토큰도 변경되기 때문에, 외부의 악성 애플리케이션에서는 이 값을 알 수 없습니다.

현재 세션의 CSRF 토큰은 요청의 세션을 통해서나, 또는 csrf_token 헬퍼 함수를 통해 가져올 수 있습니다:

use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
$token = $request->session()->token();

$token = csrf_token();

// ...
});

애플리케이션에서 "POST", "PUT", "PATCH", 또는 "DELETE" HTML 폼을 정의할 때마다, CSRF 보호 미들웨어가 요청을 검증할 수 있도록 폼 안에 숨겨진 CSRF _token 필드를 반드시 포함해야 합니다. 편리하게도, Blade의 @csrf 디렉티브를 사용하면 이 숨겨진 토큰 입력 필드를 쉽게 생성할 수 있습니다:

<form method="POST" action="/profile">
@csrf

<!-- Equivalent to... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>

web 미들웨어 그룹에 기본 포함되어 있는 App\Http\Middleware\VerifyCsrfToken middleware는 요청 입력값의 토큰과 세션에 저장된 토큰이 일치하는지를 자동으로 확인합니다. 두 토큰이 일치하면, 해당 요청이 실제 인증된 사용자가 수행한 것임을 신뢰할 수 있습니다.

CSRF Tokens & SPAs​

Laravel을 API 백엔드로 사용하는 SPA(Single Page Application)를 개발하고 있다면, API 인증 및 CSRF 취약점 방지에 관한 자세한 내용은 Laravel Sanctum documentation를 참고하시기 바랍니다.

Excluding URIs From CSRF Protection​

경우에 따라 일부 URI를 CSRF 보호 대상에서 제외하고 싶을 때가 있습니다. 예를 들어, Stripe를 통한 결제 처리를 위해 Stripe의 웹훅(Webhook) 시스템을 사용한다면, Stripe가 여러분의 경로로 CSRF 토큰을 보낼 수 없기 때문에 웹훅을 처리하는 라우트를 CSRF 보호에서 반드시 제외해야 합니다.

보통 이런 라우트는 App\Providers\RouteServiceProvider가 routes/web.php의 모든 라우트에 기본 적용하는 web 미들웨어 그룹 바깥에 배치하는 것이 좋습니다. 하지만, VerifyCsrfToken 미들웨어의 $except 속성에 해당 URI를 추가하는 방식으로도 제외할 수 있습니다:

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}

Laravel에서 running tests을 할 때는 편의를 위해 모든 라우트에 대해 CSRF 미들웨어가 자동으로 비활성화됩니다.

X-CSRF-TOKEN​

POST 파라미터로 CSRF 토큰 값을 검사하는 것 외에도, App\Http\Middleware\VerifyCsrfToken 미들웨어는 X-CSRF-TOKEN 요청 헤더도 함께 검사합니다. 예를 들어, HTML의 meta 태그에 토큰을 저장해 둘 수도 있습니다:

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

이후 jQuery와 같은 라이브러리에서 모든 요청 헤더에 토큰값을 자동으로 추가하도록 설정할 수 있습니다. 이 방식은 레거시 JavaScript 기술로 구현된 AJAX 기반 애플리케이션에 간편한 CSRF 보호를 제공합니다:

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

X-XSRF-TOKEN​

Laravel은 현재 CSRF 토큰을 암호화된 XSRF-TOKEN 쿠키에 저장하여 프레임워크가 생성하는 모든 응답에 포함시킵니다. 이 쿠키의 값을 읽어서 X-XSRF-TOKEN 요청 헤더에 넣어 사용할 수 있습니다.

이 쿠키는 개발자 편의를 위해 제공되는 것이며, Angular, Axios와 같은 일부 JavaScript 프레임워크나 라이브러리가 동일 출처 요청(same-origin request)에 대해 쿠키 내 값을 자동으로 X-XSRF-TOKEN 헤더에 추가해 줍니다.

기본적으로 resources/js/bootstrap.js 파일에는 Axios HTTP 라이브러리가 포함되어 있으며, 이 라이브러리는 X-XSRF-TOKEN 헤더를 자동으로 전송합니다.