Laravel で Content-Security-Policy ヘッダーを出力してみる
前書き
Laravel で Content-Security-Policy ヘッダーを出力してみました、という、平凡な話です。
Content-Security-Policy ヘッダーとは、セキュリティを向上させる為のヘッダーで、例えばインラインで書いた JS などの実行を禁止するようブラウザに伝えたりする為のものです(XSS対策等)。他にも色々ある為、詳しくは「Content-Security-Policy ヘッダー」で検索お願いします。
そもそもヘッダーの出力であれば、Webサーバ側や.htaccess を使えば、簡単にできたりするのですが、Content-Security-Policy(以下、CSP)の場合、「このページでは、こう出力したい!」なんてこともあったりするので、プログラム側で制御できると良い場面があるかと思ったりします。
既に、CSP を管理できるOSSライブラリに spatie/laravel-csp というのがあったりして、これ使えば細かく制御できそうですが、今回は自作で、融通の利かないもの😄をサクッと作ってみたいと思います。とはいえ、ミドルウェア使って、ヘッダーを出力するだけですが😓
本題
ということで、まずはミドルウェアを作成します。下記の感じです。
php artisan make:middleware ContentSecurityPolicy
中身は後で見るとして、まずは web.php のグループとかで、このミドルウェアを指定します。
<?php
use App\Http\Middleware\ContentSecurityPolicy;
use Illuminate\Support\Facades\Route;
Route::middleware(ContentSecurityPolicy::class)->group(function () {
Route::get('hoge', fn () => 'hoge');
Route::get('bar', fn () => 'bar');
Route::get('baz', fn () => 'baz');
Route::get('foo', fn () => 'foo')->withoutMiddleware(ContentSecurityPolicy::class);
// その他
});
これで、hoge, bar, baz の URL の時は、CSP ヘッダーが出力されます。
foo の場合は、withoutMiddleware() しているので、出力されません。(参考までに)
ContentSecurityPolicy ミドルウェアは、例えば以下の感じです。(Docblockは略)
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class ContentSecurityPolicy
{
public function handle(Request $request, Closure $next)
{
$response = $next($request);
if ($response->isServerError()) {
return $response; // デバッグ画面表示の為
}
$csp = <<<'END'
default-src 'self';
style-src 'self' 'unsafe-inline' www.example.net;
img-src 'self' foo.example.com;
script-src 'self';
END;
$csp = preg_replace('~(\s)+~u', ' ', trim($csp)); // スペースを整理して1行に
$response->headers->set('Content-Security-Policy', $csp);
return $response;
}
}
最初の if 文は、デバッグ画面の時には、CSP を出力しない為です。出力すると、デバッグ画面が表示されなくなってしまいます。
そして、正常な時は CSP ヘッダーを出力します。
ヘッダーは1行で書かないといけないのですが、見やすくする為に一度複数行で書き、その後整形して1行にしています。
雑感
とりあえずこんな感じになりました。'self' など以外に 'nonce' とかいうのもあったりして、結構便利です。(今回、割愛してしまいました…)
CSP は、メタタグで記述する事もでき、その方法も場合によってはいいかも知れません。
不具合等ありましたらコメント下さい。
Discussion