Helmetの保護機能の無効化(frameguard/xFrameOptions/CSP)

セキュリティヘッダーの不適切な設定(Helmet)

説明

HelmetはExpressアプリケーションにセキュリティ用のHTTPヘッダーを設定します。フレーム保護のオプション名は、バージョンによって frameguard や xFrameOptions など異なります。このオプションや contentSecurityPolicy(CSP)を false にすると、該当するヘッダーを設定しなくなります。ほかの層で同等のポリシーを提供していない場合、クリックジャッキングやスクリプト注入への防御が弱まるおそれがあります。CSPを無効にするだけでXSSが発生するわけではありません。

想定される影響

  • フレームへの埋め込み制限がないと、攻撃者がページをiframeに埋め込み、意図しない操作をクリックさせる可能性があります。
  • 有効なCSPがないと、スクリプト注入の脆弱性がある場合に、悪意あるスクリプトの読み込みや実行を制限する防御が減ります。

対処方法

  • 可能なら app.use(helmet()) の既定の保護を維持してください。アプリケーションやプロキシが実際に返すヘッダーを確認してください。
  • Helmetのバージョンに合った X-Frame-Options またはCSPの frame-ancestors で、埋め込みを許可するオリジンを制限してください。保護範囲が重なるため、最終応答のポリシーを併せて確認してください。
  • CSPを必要なソースに限定してください。広範な *、'unsafe-inline'、'unsafe-eval' の許可を避け、必要に応じてnonceやハッシュによるポリシーを使ってください。
  • 外部フレームやスクリプトが必要なページにだけ例外を適用してください。

例

変更前

javascript
const express = require("express");
const helmet = require("helmet");
const app = express();

// 変更前: このミドルウェアでフレーム保護ヘッダーとCSPを両方無効にします。
app.use(helmet({
  xFrameOptions: false,
  contentSecurityPolicy: false,
}));

app.get("/pay", (req, res) => {
  res.send('<button id="pay">결제하기</button>');
});

app.listen(3000);

変更後

javascript
const express = require("express");
const helmet = require("helmet");
const app = express();

// 変更後: Helmetを有効にし、CSPとフレーム保護を制限的に設定します。
app.use(
  helmet({
    // 古いHelmetではframeguard: { action: "sameorigin" }を使います。
    xFrameOptions: { action: "sameorigin" },
    contentSecurityPolicy: {
      useDefaults: true,
      directives: {
        "default-src": ["'self'"],
        "script-src": ["'self'"], // インラインスクリプトと任意の外部ソースを許可しません。
        "object-src": ["'none'"],
        "frame-ancestors": ["'self'"], // 埋め込み元のオリジンを制限します。
      },
    },
  })
);

app.get("/pay", (req, res) => {
  res.send('<button id="pay">결제하기</button>');
});

app.listen(3000);

説明:

  • 変更前: 一つのHelmet設定で X-Frame-Options とCSPの両方を無効にします。別の層で提供しなければ、応答にこの二つのヘッダーは付きません。
  • 変更後: 同一オリジンのフレームとスクリプトだけを許可します。サービスの要件とHelmetのバージョンに合わせて調整してください。

参考資料