[Laravel 기본] 10. CSRF Protection

CSRF ?? (Cross Site Request Forgery)

CSRF 는 Cross Site Request Forgery 의 약자로 크로스 사이트 요청 위조라고 불립니다. 악의적으로 사이트를 요청을 위조해서 서버에 위해를 가하는 웹 사이트 공격 방법입니다. OWASP 에서 선정한 웹 어플리케이션 보안 취약점 10가지 중 하나이다. CSRF 를 이해하기 쉽게 그림으로 살펴보겠습니다.

CSRF 공격 패턴
  1. 악의적인 의도를 품은 Attacker가 Application 사용자 대상으로 XSS(크로스 사이트 스크립팅), 피싱 사이트로 공격을 실행합니다.
  2. 권한이 있는 User는 Attacker의 공격에 따라 어플리케이션에 주로 데이터 변경과 관련된 (POST, PUT, DELETE Method) 요청을 하게 합니다.
  3. 이 요청을 통해 Attacker는 어플리케이션에서 User Cookie, Session 등 정보를 탈취하거나 어플리케이션에 치명적인 손상을 줍니다.

CSRF Protection

이런 공격 패턴을 지니고 있는 CSRF 공격에는 어떤 방어 방법들이 있는 간략하게 알아보겠습니다.

1. HTTP referer Checking

제일 간단한 방법입니다. HTTP Header 중 이전 도메인에 대한 정보를 가지고 있는 HTTP referer 를 통해 요청 도메인에 대한 분석을 통해 접근을 제한하는 방법입니다. 하지만 같은 도메인 내에서 XSS 공격을 하는 경우 취약점이 들어나며 referer 변조를 통해 취약점이 들어나기도 합니다.

2. Synchronizer token pattern

<input type="hidden" name="csrfmiddlewaretoken" value="KbyUmhTLMpYj7CD2di7JKP1P3qmLlkPt" />

위의 소스 코드와 같이 form 태그 아래 Token를 숨겨 이 토큰을 통해 서버에 있는 토큰과 비교하여 CSRF 공격 여부를 판단하는 방법입니다. token 이 일치하지 않는 경우 요청을 무시합니다.

3. Cookie-to-header token

Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/Ti

Token를 사용하는 점에서 위의 Synchronizer token 방어 방법과 비슷합니다만 Cookie header 에 csrf token를 같이 보내는 방법입니다. 사실 토큰 정보를 탈취하여 Cookie를 수정하고 보내는 경우에는 위험하다고 생각할 수 있으나 web browser 의 Same Origin 원칙에(동일 출처 정책) 의존하여 이를 방지하는 취약점 보호 방법입니다.

4. Double Submit Cookie

Cookie-to-header 방식과 비슷하지만 차이가 있습니다. CSRF Token를 쿠키에 Set하고 form에 hidden field를 보냅니다. 이 form 이 전송될때 사이트는 Cookie 에 있는 토큰값고 매칭되는지 체크합니다. 이 방식 또한 Same Origin 원칙에 의해 쿠키 정보에 접근하는 것을 막을 수 있습니다. Synchronizer pattern 과 다르게 서버에 토큰 정보를 저장하지 않아도 된다는 장점이있습니다.

Laravel CSRF Protection

Laravel 프레임워크는 다른 프레임워크(Ruby on rails 등) 처럼 자동으로 CSRF Protection 를 제공합니다. CSRF Protection 방법 중 어떤 것으로 구현하고 있는지 생각하면서 Laravel CSRF Protection 에 대해 익혀보겠습니다.

FORM 태그 @CSRF 사용

<form method="POST" action="/profile">
    @csrf // = <input type='hidden' name="_token" value="AWE4636AETAetcae324">
    ...
</form>

form 태그 안에 @csrf 입력하면 Blade 라는 템플릿 엔진을 통해 hidden field 와 토큰을 생성합니다. crsf token 과 더불어 요청이 들어오면 라라벨은 web group middleware 인 VerifyCsrfToken 를 통해 세션에서 토큰을 비교하여 취약점을 보호합니다.

X-CSRF-TOKEN

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

Javascript 를 이용해 비동기로 요청하는 경우에는 위처럼 form 태그안에 @csrf 를 이용하는 대신 다른 방법으로 대신합니다. meta 태그에 csrf_token를 추가하고 비동기 요청을 진행할 시 라라벨에 내장되어 있는 axios 라이브러리를 사용할 경우에는 request header 에 ‘X-CSRF-TOKEN’ 에 토큰 정보를 같이 전송하게 됩니다. bootstrap.js 파일을 살펴보면 이해가 쉽습니다.

// bootstrap.js

// ... //
// meta tag의 정보를 읽고 default 로 헤더에 X-CRSF-TOKEN 를 포함시킨다.
let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}
// ... //

X-XSRF-TOKEN

VertifyCsrfToken 미들웨어는 Response cookie에 X-XSRF-TOKEN 라는 이름으로 토큰 정보를 저장합니다. ($addHttpCookie 값으로 쿠키 저장 여부 결정할 수 있다.) 사용자는 이 쿠키의 토큰을 가지고 편리하게 요청을 할 수 있다. VeritfyCsrfToken 미들웨어 소스 코드를 열어보면 Laravel CSRF token 이해에 도움이 됩니다.

<?php

namespace Illuminate\Foundation\Http\Middleware;

class VerifyCsrfToken
{
    // crsf token protection 이 필요없는 url를 정의할 수 있다.
    protected $except = [];

    protected $addHttpCookie = true;

    // tokenMatch 에서 볼 수 있듯이 session token 과 요청 토큰을 비교한다.
    // 이를 통해 Synchronizer token 방식이란 것을 알 수 있다.
    protected function tokensMatch($request)
    {
        $token = $this->getTokenFromRequest($request);

        return is_string($request->session()->token()) &&
               is_string($token) &&
               hash_equals($request->session()->token(), $token);
    }
   
    // session 정보에 접근하여 response시 Cookie X-XSRF-TOKEN 로 토큰을 저장한다.
    protected function addCookieToResponse($request, $response)
    {
        $config = config('session');

        $response->headers->setCookie(
            new Cookie(
                'XSRF-TOKEN', $request->session()->token(), $this->availableAt(60 * $config['lifetime']),
                $config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null
            )
        );

        return $response;
    }
}

주의깊게 볼만한 Method는 tokenMatch() 입니다. meta tag 또는 hidden field 또는 X-XSRF-TOKEN 의 Request 로 넘어온 token 값을 Session 토큰과 비교하여 취약점을 보완하고 있습니다. Laravel은 위에서 본 보호 방법 중 Synchronizer Pattern 를 사용하여 CSRF Protection 를 구현하고 있다는 것을 알 수 있습니다.

TL;DR

  • CSRF (Cross Site Request Forgery) 은 악의적으로 사이트 요청을 위조하여 웹 어플리케이션에 치명적인 손상이나 유저에게 피해를 입히는 공격입니다.
  • Laravel 에서는 CRSF 보호를 위해 token 방식을 이용하며 이 토큰들을 자동으로 생성하고 요청에 포함시켜 사용합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다