라라벨에서 로그아웃 후 브라우저 뒤로가기로 이전 페이지 접근 방지하는 방법, 캐싱 문제 해결

라라벨11에서 로그아웃 후 뒤로가기로 이전 페이지 노출 문제를 해결하는 방법과 캐시 무효화 방식의 장단점을 설명합니다. 캐시 무효화로 보안은 강화되지만 성능 저하와 서버 부하 증가의 단점이 있어 상황에 맞게 적용해야 합니다.

라라벨에서 로그아웃 후 브라우저 뒤로가기로 이전 페이지 접근 방지하는 방법, 캐싱 문제 해결
Photo by Mohammad Rahmani / Unsplash

로그아웃 후에 브라우저 뒤로가기 버튼을 눌렀을 경우, 여전히 로그인 상태에서 봤던 이전 페이지가 드러나는 문제가 있습니다.

라라벨11 프레임워크를 이용해 웹사이트를 만드는 상황이고, laravel breeze를 이용해서 인증을 손쉽게 구현할 수 있어 참 감동 받은 상태였습니다(?)

그런데 이것저것 테스트하다보니 희한하게도 로그아웃을 했는데도 브라우저 뒤로가기 버튼으로 로그인 됐었던 대시보드 화면이 다시 나타나는 겁니다. 리액트 같은 것만 사용하다보니 세션기반 인증에 대한 경험이 없어서 무척 당황했었는데요.

일단 근원적인 문제는 laravel breeze에서 구현한 소스대로 작동하는게 정상작동이긴 합니다. 서버에서 세션 정보는 이미 만료 되었기때문에, 뒤로 돌아간다해도 무언가 데이터를 다루는 행위는 불가능하기 때문이죠.

그런데 문제는 아무리 조작이 불가능한 상태라고해도 로그아웃을 했음에도 정보가 드러난다는 점입니다.

뒤로 가기를 했을때 이전 페이지가 노출되는 이유는 브라우저가 서버에 요청하지 않고 사용자의 PC에 저장된 캐시 정보를 그대로 불러와서 노출 시키는 작동때문에 벌어지는 현상이더군요.

그래서 검색을 해보니 이런 문제에 대해서 질문 답변을 공유한게 상당히 많더군요. 십수년전 기록부터 나란히 주욱 나옵니다. 그 중에서 자바스크립트를 이용하여 제어하는 방식이 가장 간소해보였으나, 자바스크립트는 뭔가 좀 못 미더운 구석이 있어서 서버 측에서 처리할 수 있는 방법들을 선택했습니다.


라라벨(현재 버전 11기준)에서 이 문제를 해결하려면 일단 미들웨어를 하나 생성해줍니다.

php artisan make:middleware NoCacheHeaders

생성된 NoCacheHeaders 미들웨어에서 아래와 같이 내용을 추가해줍니다.

<?php
// app/Http/Middleware/NoCacheHeaders.php

namespace App\Http\Middleware;
(...중략...)
    public function handle(Request $request, Closure $next): Response
    {
    	// 여기서부터 추가
        $response = $next($request);

       $response->headers->set('Cache-Control', 'no-cache, must-revalidate, no-store, max-age=0, private');
        $response->headers->set('Pragma', 'no-cache');
        $response->headers->set('Expires', 'Fri, 01 Jan 1990 00:00:00 GMT');
        
        return $response;
        // 완료
    }
}

Routes/web.php에 가서 라우트 미들웨어에 방금 만든  NoCacheHeaders 미들웨어를 추가해줍니다.

<?php
// routes/web.php
(...중략 ...)
use App\Http\Middleware\NoCacheHeaders;

// NoCacheHeaders::class를 미들웨어에 추가
Route::middleware(['auth', 'verified', NoCacheHeaders::class])->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

require __DIR__ . '/auth.php';

이렇게 처리를 해주고나면 이제 기대한대로, 로그아웃 후에도 이전페이지가 들어나지 않도록 처리가 됩니다.

이 방식은 캐시 자체를 매번 새로 받아오는 방식으로 해결하는 방법입니다. 이 방법도 장단점이 있습니다.

장점

  1. 확실한 캐시 무효화 보장. 항상 서버에서 최신 응답을 가져옵니다.
  2. 개인 정보 보호 및 보안 강화. private로 인해 공유 캐시에 저장이 안되고, no-store로 인해 로컬 캐시에도 저장되지 않습니다.
  3. 데이터 신선도 보장. must-revalidatemax-age=0으로 인해 캐시된 응답의 유효성을 항상 재검증합니다.

단점

  1. 성능 저하 발생. 매번 서버로부터 응답을 가져오므로 지연시간이 발생할 수밖에 없습니다.
  2. 대역폭 사용량 증가. 마찬가지로 캐시를 사용하지 않으므로 서버로의 요청이 많아집니다.
  3. 서버 부하 증가. 항상 서버에 새로운 요청을 하기때문에 서버 부하가 높아질 수 있습니다.

그래서 보안 측면에서는 장점이지만, 서버에 과부하를 유발하고, 속도면에서는 손해를 보는 방법이니 어느쪽에 더 집중해야하는지에 따라서 페이지별로 구분해서 쓰는 등, 다양한 접근방법을 연구해보는 것이 좋을 것 같습니다.

아래 링크들은 본 포스팅을 작성하는데 참고한 자료들입니다.


Laravel logout, hittin gthe back button send s me back into the app, how do I prevent this
Currently I use the default laravel login/auth controllers, routes, views and no custom logic. We are building an app where if you logout and click the back button you cannot, under any circumstanc...
--
[etc] Logout 후 뒤로 가기 방지 - BF Cache
문제 logout을 한 뒤에 로그인 권한이 필요한 페이지로 뒤로 가기를 눌렀을 때, 데이터가 그대로 노출되는 문제 보안상의 결함으로 수정해야하는 경우 원인 브라우저에서 관리하는 BF Cache(Back-Forward Cache) 웹 페이지를 방문하면 해당 페이지의 css/html/javascript/image 등 모든 리소스를 캐시에 저장 뒤로가기 / 앞으로 가기 버튼을 누르면 서버에 다시 요청하지 않고 캐시 리소스 활용해서 로딩 로그아웃 이후에도 BF Cache가 남아있기 때문에 다시 페이지가 보일 수 있음 해결 방법(Fast A…