미들웨어란?

라라벨 4에서는 필터(filters) 라는 이름으로 제공되었으나 5 에서 미들웨어(Middleware) 로 변경되었으며 애플리케이션에 들어온 요청을 필터링할수 있는 기능을 제공합니다.

미들웨어는 애플리케이션보다 먼저 실행시킬 수 있으므로 인증 여부 확인등 공통 기능을 편리하게 구현할 수 있습니다.

 

예로 자기의 프로필을 수정하는 기능이 있을 경우 레거시 방식으로 개발할 경우 다음과 같은 절차를 거쳤습니다.

  1. 로그인 여부를 확인하는 공통 코드를 담고 있는 login_check.php 를 작성

    <?php
    session_start();
    if (!(isset($_SESSION['login']) && $_SESSION['login'] != '')) {
        header ("Location: login.php");
    	return;
    }
    ?>
    CODE
  2. 로그인이 필요한 애플리케이션 소스(예: user/profile_edit.php) 마다 다음과 같이 맨 위에 login_check.php 를 삽입하고 그 후에 실제 애플리케이션 로직을 작성했습니다.

    <?php
    include_once('login_check.php');
     
    // app 로직 시작
    CODE

위와 같은 방식은 새로운 기능을 구현할 경우 매번 login 을 확인하는 코드를 넣어줘야 한다는 사실을 개발자가 알고 있어야 하고 실수로 빠뜨렸을 경우 로그인 없이 사용이 가능한 문제가 발생합니다.

 

미들웨어를 사용한다면 실제 애플리케이션(profile/edit) 소스에 로그인 여부를 일일이 작성하지 않고 로그인 확인 미들웨어를 설정하고 미들웨어내에서 사용자가 인증 되지 않았다면 로그인 페이지로 redirect 하고 인증 되었다면 요청한 애플리케이션(프로필 수정 화면)으로 제어권을 넘겨주도록 일관성 있게 인증 작업을 수행할 수 있습니다.

 

사전에 정의된 미들웨어의 용도는 다음과 같습니다.

  • RedirectIfAuthenticated위에서 설명한 인증 여부를 확인하고 인증되지 않았을 경우 인증 페이지로 redirect 합니다.
  • Authenticate: 사용자 인증을 처리하는 미들웨어 입니다.
  • EncryptCookies: 보안을 위해 쿠키를 암호화합니다.
  • VerifyCsrfToken사이트 간 요청 위조(CSRF 또는 XSRF; Cross-Site Request Forgery) 공격 방지를 위해 자동으로  CSRF 토큰을 검증하며 자세한 내용은 아래에서 다룹니다.
  • CheckForMaintenanceMode:  매 요청시마다 사이트가 정비 모드인지 확인합니다.

 

artisan 절에서 설명한 php artisan down 명령어로 사이트를 정비 모드로 전환할 경우 storage/framework/down 파일이 생성되며 이 파일이 있을 경우 미들웨어인 CheckForMaintenanceMode 가 HTTP 503 응답을 전송하므로 모든 요청은 애플리케이션에 전달되지 않게 됩니다.

이렇게 미들웨어는 애플리케이션보다 먼저 호출되므로 공통 처리 로직이 필요할 경우 애플리케이션을 수정하지 않고 미들웨어를 만들어주면 되므로 비즈니스 로직에 더 집중할 수 있는 장점이 있지만 매번 호출되므로 속도가 느려지는 단점이 있습니다.

저자의 의견은 "프레임워크 없는 개발" 과 "라라벨같은 프레임워크를 사용한 개발" 의 비교처럼 속도 저하로 인한 단점은 서버의 하드웨어 용량을 늘리거나 물리적인 서버 대수를 추가하여 해결하면 되며 이런 단점보다는 개발생산성이 뛰어나고 실수 여지가 적어지고 비즈니스 로직에 집중할 수 있는등 미들웨어를 사용하는 것이 장점이 훨씬 크다고 봅니다.

 

미들웨어 설정

미들웨어 설정은 app/Http/Kernel.php 파일을 통해서 할 수 있으며 이 파일의 $middleware 변수에는 활성화할 전역 미들웨어가 등록되어 있습니다. 

/* 모든 요청시마다 호출되는 전역 미들웨어 */
protected $middleware = [
    \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
];
 
/* 애플리케이션 미들웨어 그룹 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],

    'api' => [
        'throttle:60,1',
    ],
];
 
/* 애플리케이션 미들웨어 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
CODE

첫번째 변수인 $middleware 는 모든 요청마다 호출되는 전역 미들웨어로 사이트가 유지모드인지 확인하는 CheckForMaintenanceMode  가 등록되어 있습니다.

 

두 번째 $middlewareGroups라라벨 5.2에 추가된 미들웨어 그룹 기능으로 다수의 미들웨어를 그룹화하여 라우팅에 할당할 수 있습니다.

webapi 두 개의 미들웨어 그룹이 있으며 web 미들웨어는 일반적인 웹 애플리케이션 개발시 필요한 기능을 묶은 그룹이며 api 는 API 서버 개발시 필요한 미들웨어를 묶은 그룹입니다.

미들웨어 그룹을 사용하려면 아래와 같이 Route::group 의 배열 파라미터의 요소로 'middleware' => ['web', 'my-mw'] 처럼 사용하면 됩니다.미들웨어 이름을 배열로 넘겨주면 됩니다.

Route::group(['middleware' => ['web', 'my-mw']], function () {
	Route::resource('project', 'ProjectController');
});
CODE


 

CSRF 토큰 검증

web 미들웨어 그룹의 제일 마지막에 VerifyCsrfToken 미들웨어가 보일 겁니다.

이것은 "사이트간 요청 위조" 공격을 막기 위한 미들웨어로 정보를 변경하는 HTTP 액션인 POST, PUT/PATCH, DELETE 요청이 들어올 때마다 자동으로 실행되는 미들웨어입니다. (CSRF 를 방지하기 위한 라라벨의 기능은2부 실전 프로젝트에서 다룹니다.)

 

마지막 $routeMiddleware  는 애플리케이션 라우팅에 등록하는 미들웨어로 용도에 맞게 개별 등록하면 되며 이 부분은 2부에서 실제 미들웨어를 만들면서 다루겠습니다.

 

그러면 VerifyCsrfToken 이 어떻게 동작하는지 확인하기 위해 피들러를 통해 PUT 요청을 생성해 보겠습니다.

  1. 그림처럼 Composer 탭을 클릭한 후에 Parsed 탭을 클릭합니다.
  2. HTTP 요청 리스트 박스에서 PUT 을 선택하고 URL 에는 주문을 변경하는 REST URL 인 http://homestead.app/orders/3 을 입력합니다.
  3. Execute 를 클릭하여 서버에 PUT 요청을 전송합니다.

전송이 완료되면 좌측 상단에 HTTP 상태 코드가 표시되며 이 경우 500 에러가 발생할 것입니다. WebView 탭을 클릭하면 서버의 응답을 브라우저를 통해 볼 수 있습니다.

위는 개발시 자주 발생하는 예외로 HTTP POST/PUT/DELETE 요청시 유효한 CSRF 토큰을 같이 전달해야 하지만 누락되었거나 유효하지 않은 토큰이 왔을 경우 발생합니다.

5.0에서는 모든 요청에 대해 CSRF 토큰 검증을 수행했지만 라라벨 5.1 부터는 CSRF 토큰을 확인하지 않는 URL 을 설정할 수 있는 기능이 포함 되었습니다.

 

app/Http/Middleware/VerifyCsrfToken.php 을 열고 $except 변수에 예외 처리를 할 URL 을 배열로 지정해 주면 됩니다. 다음은 orders 로 시작하는 url 의 모든 요청은 CSRF 토큰 검증을 하지 않겠다는 설정입니다.

app/Http/Middleware/VerifyCsrfToken.php

 class VerifyCsrfToken extends BaseVerifier {
    protected $except = [
        'orders/*', // 제외할 url 지정
        ]
PHP

이제 파일을 저장하고 다시 피들러에서 PUT 요청을 전송하면 HTTP 200 응답 코드가 오는 것을 확인할 수 있습니다.

경고

이 예제에서는 CSRF 미들웨어의 동작을 설명하기 위해 토큰 검증 비활성화 방법을 설명한 것이고 실제 애플리케이션을 개발한다면 반드시 CSRF 토큰 검증 기능을 넣는게 보안상 안전합니다.

웹훅(WebHook) 같이 특별하게 동작하는 기능일 경우에만 CSRF 예외 처리를 해야 합니다.

 

마치며

이 장에서는 라라벨 아키텍처중 핵심 요소인 서비스 프로바이더와 파사드를 설명하고 라라벨을 관리하기 위한 명령행 유틸인 artisan 사용법에 대해서 알아 보았습니다.

그리고 컨트롤러를 작성하고 이를 라우트와 연결하는 법과 미들웨어에 대해서 알아보았습니다. 다음 장에서는 라라벨이 제공하는 데이타베이스의 핵심 기능들에 대해서 공부해 보겠습니다.

미들웨어란?

라라벨 4에서는 필터(filters) 라는 이름으로 제공되었으나 5 에서 미들웨어(Middleware) 로 변경되었으며 애플리케이션에 들어온 요청을 필터링할수 있는 기능을 제공합니다.

미들웨어는 애플리케이션보다 먼저 실행시킬 수 있으므로 인증 여부 확인등 공통 기능을 편리하게 구현할 수 있습니다.

 

예로 자기의 프로필을 수정하는 기능이 있을 경우 레거시 방식으로 개발할 경우 다음과 같은 절차를 거쳤습니다.

  1. 로그인 여부를 확인하는 공통 코드를 담고 있는 login_check.php 를 작성

    <?php
    session_start();
    if (!(isset($_SESSION['login']) && $_SESSION['login'] != '')) {
        header ("Location: login.php");
    	return;
    }
    ?>
    CODE
  2. 로그인이 필요한 애플리케이션 소스(예: user/profile_edit.php) 마다 다음과 같이 맨 위에 login_check.php 를 삽입하고 그 후에 실제 애플리케이션 로직을 작성했습니다.

    <?php
    include_once('login_check.php');
     
    // app 로직 시작
    CODE

위와 같은 방식은 새로운 기능을 구현할 경우 매번 login 을 확인하는 코드를 넣어줘야 한다는 사실을 개발자가 알고 있어야 하고 실수로 빠뜨렸을 경우 로그인 없이 사용이 가능한 문제가 발생합니다.

 

미들웨어를 사용한다면 실제 애플리케이션(profile/edit) 소스에 로그인 여부를 일일이 작성하지 않고 로그인 확인 미들웨어를 설정하고 미들웨어내에서 사용자가 인증 되지 않았다면 로그인 페이지로 redirect 하고 인증 되었다면 요청한 애플리케이션(프로필 수정 화면)으로 제어권을 넘겨주도록 일관성 있게 인증 작업을 수행할 수 있습니다.

 

사전에 정의된 미들웨어의 용도는 다음과 같습니다.

  • RedirectIfAuthenticated위에서 설명한 인증 여부를 확인하고 인증되지 않았을 경우 인증 페이지로 redirect 합니다.
  • Authenticate: 사용자 인증을 처리하는 미들웨어 입니다.
  • EncryptCookies: 보안을 위해 쿠키를 암호화합니다.
  • VerifyCsrfToken사이트 간 요청 위조(CSRF 또는 XSRF; Cross-Site Request Forgery) 공격 방지를 위해 자동으로  CSRF 토큰을 검증하며 자세한 내용은 아래에서 다룹니다.
  • CheckForMaintenanceMode:  매 요청시마다 사이트가 정비 모드인지 확인합니다.

 

artisan 절에서 설명한 php artisan down 명령어로 사이트를 정비 모드로 전환할 경우 storage/framework/down 파일이 생성되며 이 파일이 있을 경우 미들웨어인 CheckForMaintenanceMode 가 HTTP 503 응답을 전송하므로 모든 요청은 애플리케이션에 전달되지 않게 됩니다.

이렇게 미들웨어는 애플리케이션보다 먼저 호출되므로 공통 처리 로직이 필요할 경우 애플리케이션을 수정하지 않고 미들웨어를 만들어주면 되므로 비즈니스 로직에 더 집중할 수 있는 장점이 있지만 매번 호출되므로 속도가 느려지는 단점이 있습니다.

저자의 의견은 "프레임워크 없는 개발" 과 "라라벨같은 프레임워크를 사용한 개발" 의 비교처럼 속도 저하로 인한 단점은 서버의 하드웨어 용량을 늘리거나 물리적인 서버 대수를 추가하여 해결하면 되며 이런 단점보다는 개발생산성이 뛰어나고 실수 여지가 적어지고 비즈니스 로직에 집중할 수 있는등 미들웨어를 사용하는 것이 장점이 훨씬 크다고 봅니다.

 

미들웨어 설정

미들웨어 설정은 app/Http/Kernel.php 파일을 통해서 할 수 있으며 이 파일의 $middleware 변수에는 활성화할 전역 미들웨어가 등록되어 있습니다. 

/* 모든 요청시마다 호출되는 전역 미들웨어 */
protected $middleware = [
    \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
];
 
/* 애플리케이션 미들웨어 그룹 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ],

    'api' => [
        'throttle:60,1',
    ],
];
 
/* 애플리케이션 미들웨어 */
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
CODE

첫번째 변수인 $middleware 는 모든 요청마다 호출되는 전역 미들웨어로 사이트가 유지모드인지 확인하는 CheckForMaintenanceMode  가 등록되어 있습니다.

 

두 번째 $middlewareGroups라라벨 5.2에 추가된 미들웨어 그룹 기능으로 다수의 미들웨어를 그룹화하여 라우팅에 할당할 수 있습니다.

webapi 두 개의 미들웨어 그룹이 있으며 web 미들웨어는 일반적인 웹 애플리케이션 개발시 필요한 기능을 묶은 그룹이며 api 는 API 서버 개발시 필요한 미들웨어를 묶은 그룹입니다.

미들웨어 그룹을 사용하려면 아래와 같이 Route::group 의 배열 파라미터의 요소로 'middleware' => ['web', 'my-mw'] 처럼 사용하면 됩니다.미들웨어 이름을 배열로 넘겨주면 됩니다.

Route::group(['middleware' => ['web', 'my-mw']], function () {
	Route::resource('project', 'ProjectController');
});
CODE


 

CSRF 토큰 검증

web 미들웨어 그룹의 제일 마지막에 VerifyCsrfToken 미들웨어가 보일 겁니다.

이것은 "사이트간 요청 위조" 공격을 막기 위한 미들웨어로 정보를 변경하는 HTTP 액션인 POST, PUT/PATCH, DELETE 요청이 들어올 때마다 자동으로 실행되는 미들웨어입니다. (CSRF 를 방지하기 위한 라라벨의 기능은2부 실전 프로젝트에서 다룹니다.)

 

마지막 $routeMiddleware  는 애플리케이션 라우팅에 등록하는 미들웨어로 용도에 맞게 개별 등록하면 되며 이 부분은 2부에서 실제 미들웨어를 만들면서 다루겠습니다.

 

그러면 VerifyCsrfToken 이 어떻게 동작하는지 확인하기 위해 피들러를 통해 PUT 요청을 생성해 보겠습니다.

  1. 그림처럼 Composer 탭을 클릭한 후에 Parsed 탭을 클릭합니다.
  2. HTTP 요청 리스트 박스에서 PUT 을 선택하고 URL 에는 주문을 변경하는 REST URL 인 http://homestead.app/orders/3 을 입력합니다.
  3. Execute 를 클릭하여 서버에 PUT 요청을 전송합니다.

전송이 완료되면 좌측 상단에 HTTP 상태 코드가 표시되며 이 경우 500 에러가 발생할 것입니다. WebView 탭을 클릭하면 서버의 응답을 브라우저를 통해 볼 수 있습니다.

위는 개발시 자주 발생하는 예외로 HTTP POST/PUT/DELETE 요청시 유효한 CSRF 토큰을 같이 전달해야 하지만 누락되었거나 유효하지 않은 토큰이 왔을 경우 발생합니다.

5.0에서는 모든 요청에 대해 CSRF 토큰 검증을 수행했지만 라라벨 5.1 부터는 CSRF 토큰을 확인하지 않는 URL 을 설정할 수 있는 기능이 포함 되었습니다.

 

app/Http/Middleware/VerifyCsrfToken.php 을 열고 $except 변수에 예외 처리를 할 URL 을 배열로 지정해 주면 됩니다. 다음은 orders 로 시작하는 url 의 모든 요청은 CSRF 토큰 검증을 하지 않겠다는 설정입니다.

app/Http/Middleware/VerifyCsrfToken.php

 class VerifyCsrfToken extends BaseVerifier {
    protected $except = [
        'orders/*', // 제외할 url 지정
        ]
PHP

이제 파일을 저장하고 다시 피들러에서 PUT 요청을 전송하면 HTTP 200 응답 코드가 오는 것을 확인할 수 있습니다.

경고

이 예제에서는 CSRF 미들웨어의 동작을 설명하기 위해 토큰 검증 비활성화 방법을 설명한 것이고 실제 애플리케이션을 개발한다면 반드시 CSRF 토큰 검증 기능을 넣는게 보안상 안전합니다.

웹훅(WebHook) 같이 특별하게 동작하는 기능일 경우에만 CSRF 예외 처리를 해야 합니다.

 

마치며

이 장에서는 라라벨 아키텍처중 핵심 요소인 서비스 프로바이더와 파사드를 설명하고 라라벨을 관리하기 위한 명령행 유틸인 artisan 사용법에 대해서 알아 보았습니다.

그리고 컨트롤러를 작성하고 이를 라우트와 연결하는 법과 미들웨어에 대해서 알아보았습니다. 다음 장에서는 라라벨이 제공하는 데이타베이스의 핵심 기능들에 대해서 공부해 보겠습니다.