helmetを3から8に更新したら複数のhelmetを重ねられなくなった
Cosenseでは「helmetを重ねない」という選択をしたshokai.icon 原因がどこにあるのか探る
これまでscrapboxでは、複数のhelmet middlware(commonHelmet + appHelmetなど)をexpressに重ねて読み込ませて、前段に読み込んだものを後段のmiddlewareで上書きする使い方をしていたのですが
この使い方ががhelmet 3から8までの間にできなくなったようでした。
今回はcommonHelmetを廃止して、defaults: trueを使ってデフォルト設定に寄せるようにしたので問題ないのですが
複数のhelmetを重ねる事ができなくなったという情報は、release note等にも載っていなかったのでしょうか?
Devin.icon helmet ミドルウェアのスタッキング動作の変更について
調査の結果、複数の helmet ミドルウェアインスタンスをスタックできなくなった変更は、公式のリリースノートやチェンジログには明示的に記載されていませんでした。
詳細な調査結果
1. helmet v3.21.2 から v8.0.0 までのすべてのチェンジログを確認しましたが、この特定の変更に関する言及はありませんでした。
2. GitHub の issues や discussions を検索しましたが、この特定の動作変更に関する議論も見つかりませんでした。
3. ソースコードの分析により、この変更が発生した理由が明らかになりました:
v3.21.2 の実装
v3.21.2 では、各ミドルウェアは個別のパッケージとして実装され、reduce 関数を使用して複数のミドルウェアをスタックできる仕組みになっていました:
code:js
var stack = middlewares.reduce(function (result, middlewareName) {
// ...
return result.concat(middleware(middlewareOptions))
// ...
}, [])
return function helmet (req, res, next) {
// 再帰的に各ミドルウェアを実行
function internalNext () {
// ...
middleware(req, res, internalNext)
}
internalNext()
}
この実装により:
各ミドルウェアが個別にヘッダーを設定
後から読み込まれたミドルウェアが前のミドルウェアの設定を上書き可能
v8.0.0 の実装
v8.0.0 では、アーキテクチャが大幅に変更され、統合設定オブジェクトを使用するアプローチに変わりました:
code:typescript
function getMiddlewareFunctionsFromOptions(
options: Readonly<HelmetOptions>,
): MiddlewareFunction[] {
// ...
// 重複オプションに対する明示的なエラー処理
if ("strictTransportSecurity" in options && "hsts" in options) {
throw new Error(
"Strict-Transport-Security option was specified twice. Remove hsts to silence this warning.",
);
}
// ...
}
この実装では:
統一された設定オブジェクトを強制
同じオプションが複数回指定された場合に明示的にエラーをスロー
重複するミドルウェアオプションを防止
結論
複数の helmet ミドルウェアインスタンスをスタックできなくなった変更は、helmet ライブラリのアーキテクチャ再設計の副作用と考えられます。この変更は個別のミドルウェアパッケージから統合設定アプローチへの移行の一部でしたが、公式ドキュメントには明示的に記載されていませんでした。
現在の推奨アプローチは、shokai様が実装されたように、defaults: true を使用して単一の helmet インスタンスで包括的な設定を行うことです。これにより、複数のミドルウェアをスタックする必要がなくなります。
やはりhelmetのアーキテクチャ変更によって、複数のhelmetは重ねられなくなったらしい