라라벨 미들웨어로 중요 페이지 HTTPS로 제공하기

 

웹 애플리케이션에 브라우저와 서버간에 오가는 정보를 암호화하고 보호해야 할 기능(로그인, 결제 처리등)등이 있을 경우 SSL/HTTPS 를 사용하면 안전하게 정보를 주고 받을 수 있습니다.

최근에는 하드웨어의 사양이 충분하다면 전체 웹 애플리케이션을 HTTPS 로 제공하는 경우도 많이 있습니다.

 

전체를 HTTPS 로 제공하려면 웹 서버나 SSL 가속기등에서 HTTP 로 들어온 연결을 HTTPS 로 전환하도록 설정하면 애플리케이션의 수정없이 손쉽게 처리할 수 있습니다.

예를 들어 nginx 의 경우 다음과 같이 location 에 HTTP 301 Response와 함께 HTTPS URL 을 지정하면 애플리케이션 수정없이 모든 페이지를 HTTPS 로 제공할 수 있습니다.

server {
    listen       80;
    server_name  myserver.com;
    location / {
	    return 301 https://myserver.com$request_uri;
	}
}

 

만약 웹 서버의 설정을 수정할 수 없거나 HTTP 와 HTTPS 를 섞어서 제공해야 한다면 라라벨의 미들웨어 기능을 사용하면 애플리케이션 수정을 최소화하면서 손쉽게 HTTPS 로 서비스를 제공할 수 있습니다.

 

미들웨어 생성

artisan 의 make 의 하위 옵션중에는  미들웨어를 생성하는 옵션이 있으며 파라미터는 미들웨어의 이름입니다. 강제로  HTTPS 로 전환하는 역할을 수행하므로 미들웨어의 이름은 ForceHttps 로 명명하여 생성합니다.

$ php artisan make:middleware ForceHttps 

 

미들웨어는 app\Http\Middleware\ 폴더내에 미들웨어.php 파일로 생성되며 위에서 ForceHttps 를 미들웨어 이름으로 주었으므로 app\Http\Middleware\ForceHttps.php 파일이 생성됩니다.

이제 미들웨어 파일을 열고 다음과 같이 코딩합니다.

<?php
namespace App\Http\Middleware;
use Closure;
class ForceHttps 
{
    // HTTPS 로 제공하지 않을 URI. legacy 로 시작되는 URI 일 경우 SSL 을 강제 적용하지 않음
    protected $except = [
        'legacy/*',
    ];
    // HTTPS 로 제공하지 않을 애플리케이션 환경. 개발자 PC 에서 구동(local)할 경우와 PHPUnit 을 구동(testing)할 경우는 강제 Https 를 사용하지 않음
    protected $exceptEnv = [
        'local',
        'testing',
    ];
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
		// Https 가 아니고 제외되는 조건이 아닐 경우 강제로 HTTPS 로 포워딩
         if (!$request->secure() && !$this->shouldPassThrough($request) && !$this->envPassThrough()) {
            
            return redirect()->secure($request->getRequestUri());
        }
        return $next($request);
    }
 
	// 제외할 URI 인지 확인
    protected function shouldPassThrough($request)
    {
        foreach ($this->except as $except) {
            if ($request->is($except)) {
                return true;
            }
        }
     
        return false;
    }
 
	// 제외할 환경인지 확인
    protected function envPassThrough() 
    {
        $appEnv = \App::environment();
        foreach ($this->exceptEnv as $except) {
            if ($appEnv === $except)
                return true;
        }
        return false;  
    }
}

 

이제 생성한 미들웨어는 app/Http/Kernel.php에 등록하면 되며 라라벨에서는 두 가지 방식의 미들웨어가 있습니다.

 

전역 미들웨어(Global Middleware)

전역 미들웨어는 모든 Request 마다 호출되는 미들웨어로 호출의 오버헤드가 있지만 개별 라우트에는 등록하지 않아도 되는 장점이 있으며 기본 탑재된 전역 미들웨어로는 애플리케이션이 유지보수 상태인지 확인하는 CheckForMaintenanceMode 와 Csrf 토큰을 검증하는 VerifyCsrfToken, 쿠키를 암호화하는 EncryptCookies 등이 있습니다.

 

다음은 ForceHttps 미들웨어를 전역 미들웨어로 등록하는 예제입니다.

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \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,
		\App\Http\Middleware\ForceHttps::class, // 모든 컨텐츠에 대해 Https 로 제공
    ];

 

라우트 미들웨어(Route Middleware)

라우트 미들웨어로 등록하려면 아래와 같이 $routeMiddleware 배열에 키로 미들웨어의 별명을, 값에는 미들웨어 클래스를 적어주면 됩니다.

라우트 미들웨어는 HTTP와 HTTPS 를 혼용해서 사용해야 할 경우에 라우트 별로 https 사용 여부를 설정할 수 있으므로 유용합니다.

protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'force.https' => \App\Http\Middleware\ForceHttps::class,
    ];

 

 

이제 라우트 설정에 다음과 같이 middelware 라는 키에 미들웨어 이름(force.https)를 사용하여 https 사용 여부를 설정할 수 있습니다.

//Route::get('auth/login', 'Auth\AuthController@getLogin');
// https 미들웨어 적용
Route::get('auth/login',['middleware' => 'force.https', 'as' => 'auth.login', 'uses' => 'Auth\AuthController@getLogin']);

 

만약 여러 개의 미들웨어를 사용한다면 다음과 같이 사용할 미들웨어 이름을 배열로 넘겨주면 됩니다.

Route::get('admin/profile', ['middleware' => ['force.https', 'auth', ], function () {
    return view('profile.edit');
}]);

라우트 미들웨어로 등록할 경우 auth 미들웨어보다 force.https 를 먼저 적어 주어서 먼저 호출되도록 해야 합니다. auth 를 먼저 적으면 http 로 인증이 이루어진 후에 https 로 전환이 되므로 로그인 정보 보호가 제대로 되지 않습니다.