라라벨에서 로그아웃 후 브라우저 뒤로가기로 이전 페이지 접근 방지하는 방법, 캐싱 문제 해결
로그아웃 후에 브라우저 뒤로가기 버튼을 눌렀을 경우, 여전히 로그인 상태에서 봤던 이전 페이지가 드러나는 문제가 있습니다.
라라벨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';
이렇게 처리를 해주고나면 이제 기대한대로, 로그아웃 후에도 이전페이지가 들어나지 않도록 처리가 됩니다.
이 방식은 캐시 자체를 매번 새로 받아오는 방식으로 해결하는 방법입니다. 이 방법도 장단점이 있습니다.
장점
- 확실한 캐시 무효화 보장. 항상 서버에서 최신 응답을 가져옵니다.
- 개인 정보 보호 및 보안 강화.
private
로 인해 공유 캐시에 저장이 안되고,no-store
로 인해 로컬 캐시에도 저장되지 않습니다. - 데이터 신선도 보장.
must-revalidate
와max-age=0
으로 인해 캐시된 응답의 유효성을 항상 재검증합니다.
단점
- 성능 저하 발생. 매번 서버로부터 응답을 가져오므로 지연시간이 발생할 수밖에 없습니다.
- 대역폭 사용량 증가. 마찬가지로 캐시를 사용하지 않으므로 서버로의 요청이 많아집니다.
- 서버 부하 증가. 항상 서버에 새로운 요청을 하기때문에 서버 부하가 높아질 수 있습니다.
그래서 보안 측면에서는 장점이지만, 서버에 과부하를 유발하고, 속도면에서는 손해를 보는 방법이니 어느쪽에 더 집중해야하는지에 따라서 페이지별로 구분해서 쓰는 등, 다양한 접근방법을 연구해보는 것이 좋을 것 같습니다.
아래 링크들은 본 포스팅을 작성하는데 참고한 자료들입니다.