🍙

キャッシュさせない!!という設定の話

2023/08/21に公開

こんばんは。
今日も簡単なセキュリティ関連の記事を作成してみようと思います。

今日は、「キャッシュをさせない!!」という要件を求められた時にやる事
というテーマです。

あまり「脆弱性とかどうでも良い!!キャッシュさせるのだ!!」
という要件は聞いた事はありませんが、一応そういう要件があったと仮定します。

キャッシュをしていると何が悪いのか

  • プロキシサーバー等にキャッシュされている秘密情報が別人のブラウザに表示されてしまう
  • 他人のアカウント情報がキャッシュから返されてしまう事故
    同じコンピュータを使用すると、他人の情報を取得してしまう可能性があります

こういった問題を回避するためにも、動的なサイトを作成される方々はキャッシュ制御について理解し、正しく制御をかけていきましょう。

今回は「キャッシュはさせない!!」という強固な設定について纏めます。
こちらも要件に合わせて柔軟に切り替えていただければと思います。

結論

Cache-control: no-store Pragma: no-cache

と設定しましょう。

一つずつ詳しく見ていきます。

no-store とは

こちらは、「プライベートキャッシュ」「共有キャッシュ」どちらもレスポンスを保存しないように指示するものです。

よく間違われるのが「no-cache」ですが、こちらは「オリジンサーバーの確認なしに勝手にキャッシュしない」という設定になりますので、no-cacheはキャッシュします
今回取り上げている「キャッシュはしない!!」という断固拒否状態には適しません。
覚えておきましょうね。

Pragma: no-cache とは

こちらはHTTP/1.0において使用できるヘッダーです。上述の設定では「HTTP/1.1」にのみ対応しておりますので、旧式にも同様に対応できるように設定しています。
Pragmaヘッダーには「no-cache」のみ用意されていますので、こちらを使用します。
意味は「Cache-Control no-cache」と同じです。
但し、「Pragma: no-cache」のみ使用する事は非推奨となっておりますのでご注意ください。

実装してみましょう

では、概念の話は終わったので、実装してみましょう。
下記2パターンで実装してみます。

nginxを用いる場合

nginx.conf
add_header Cache-control "no-store";
add_header Pragma "no-cache";

動作確認をしてみましょう

設定通りにレスポンスヘッダーをつける事に成功しました!

PHP側で設定する場合

こちらはmiddlewareを用います。
まずはmiddlewareを作成しましょう。

$ php artisan make:middleware CacheControlMiddleware

作成したミドルウェアを編集していきます。

CacheControlMiddleware.php

/**
 * Handle an incoming request.
 *
 * @param Request $request
 * @param \Closure(\Illuminate\Http\Request):(\Symfony\Component\HttpFoundation\Response)  $next
 * @return Response
 */
public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);

    $response->header('Cache-Control', 'no-store');
    $response->header('Pragma', 'no-cache');

    return $response;
}

作成ができましたので、ミドルウェアを登録しましょう。

Kernel.php
/**
 * The application's route middleware groups.
 *
 * @var array<string, array<int, class-string|string>>
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\cacheControlMiddleware::class,
    ],
];

できました!
動作確認をしてみましょう。

「no-store private」
になっていますが、設定できました!

ディレクティブの意味を調べますと

レスポンスディレクティブの no-store は、あらゆる種類のキャッシュが(プライベートであろうと共有であろうと)このレスポンスを保存しないようにすることを指示します。
引用元: mdn web docs

という事なので、これで大丈夫ですね。

追伸

本日結論付けた設定は「最近のブラウザへの対策はこれで十分である
という前提に基づきます。
多くの方はこの前提条件に該当すると思いますが...

中にはno-store/no-cacheを評価しないというCDN/proxyも存在する様で、下記の設定方法も参考として掲載させていただきます。

Cache-control: private no-cache no-store must-revalidate Pragma: no-cache

矛盾しているじゃないか!!
とお思いになるかもしれませんが、ディレクティブは大まかに「制御の強いもの」から適用される様です
但し、具体的に一つずつ順番が決められているのはなく、複数のグループで定められている様です
(こちらは具体的に何が何位まで把握できませんでした...)

no-storeが強いのは明白なので、まずこれが適用されるんだなー...
程度であれば、ご理解いただけるのではないでしょうか

こちらはご参考程度にご一読いただきますとより強い制御ができるのではないでしょうか

今日はこの辺で失礼します。

参考文献

Cache-Controlとは
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Cache-Control
https://turningp.jp/network_and_security/http_header-cache
https://datatracker.ietf.org/doc/html/rfc7234#section-5.2

Pragmaヘッダーとは
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Pragma

LaravelでCache-Controlをしたい時
https://r-tech14.com/web-security/
https://github.com/laravel/framework/discussions/43460

Arsaga Developers Blog

Discussion