helmet の使い方 — Express アプリのセキュリティヘッダーを一括設定
一言でいうと
helmet は、Express アプリケーションの HTTP レスポンスヘッダーを適切に設定し、XSS・クリックジャッキング・MIME スニッフィングなど、よくある Web 攻撃からアプリを保護するセキュリティミドルウェアです。app.use(helmet()) の一行で 13 種類以上のセキュリティヘッダーをまとめて設定できます。
どんな時に使う?
- Express で API サーバーや Web アプリを構築するとき — デフォルトの Express はセキュリティ関連のヘッダーをほとんど設定しないため、helmet を入れるだけで基本的な防御が整います
- Content-Security-Policy(CSP)を導入したいとき — CSP のディレクティブを柔軟に設定でき、nonce ベースのポリシーにも対応しています
- セキュリティ監査や脆弱性スキャンで指摘を受けたとき —
X-Frame-Options、Strict-Transport-Security、X-Content-Type-Optionsなど、よく指摘されるヘッダーを一括で対応できます
インストール
# npm
npm install helmet
# yarn
yarn add helmet
# pnpm
pnpm add helmet
以下の解説は helmet v8.x 系(2024年時点の最新メジャーバージョン)に基づいています。
基本的な使い方
最もシンプルなパターンは、Express ミドルウェアとして helmet() を呼ぶだけです。
import express from "express";
import helmet from "helmet";
const app = express();
// すべてのデフォルトヘッダーを一括設定
app.use(helmet());
app.get("/", (_req, res) => {
res.send("Hello, secure world!");
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
これだけで、以下のヘッダーが自動的にレスポンスに付与されます。
| ヘッダー | デフォルト値 | 効果 |
|---|---|---|
Content-Security-Policy | default-src 'self'; ... | XSS 等の攻撃を緩和 |
Cross-Origin-Opener-Policy | same-origin | プロセス分離 |
Cross-Origin-Resource-Policy | same-origin | クロスオリジンのリソース読み込みをブロック |
Origin-Agent-Cluster | ?1 | オリジンベースのプロセス分離 |
Referrer-Policy | no-referrer | Referer ヘッダーの制御 |
Strict-Transport-Security | max-age=15552000; includeSubDomains | HTTPS の強制 |
X-Content-Type-Options | nosniff | MIME スニッフィング防止 |
X-DNS-Prefetch-Control | off | DNS プリフェッチの制御 |
X-Download-Options | noopen | IE でのダウンロード自動実行防止 |
X-Frame-Options | SAMEORIGIN | クリックジャッキング防止 |
X-Permitted-Cross-Domain-Policies | none | Adobe 製品のクロスドメイン制御 |
X-XSS-Protection | 0 | レガシー XSS フィルタを無効化(有効にすると逆に危険なため) |
また、X-Powered-By ヘッダーは削除されます。
よく使う API — helmet の使い方詳細
1. Content-Security-Policy のカスタマイズ
最も設定頻度が高いヘッダーです。directives オブジェクトで各ディレクティブを上書きできます。
import helmet from "helmet";
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
styleSrc: ["'self'", "https://fonts.googleapis.com", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https://images.example.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
connectSrc: ["'self'", "https://api.example.com"],
// null を指定するとデフォルトのディレクティブを削除
"style-src": null,
},
},
})
);
ディレクティブ名はキャメルケース(scriptSrc)でもケバブケース("script-src")でも指定できます。
2. CSP で nonce を使う
SPA やインラインスクリプトを安全に許可するための nonce パターンです。
import crypto from "crypto";
import helmet from "helmet";
// リクエストごとに nonce を生成
app.use((_req, res, next) => {
res.locals.cspNonce = crypto.randomBytes(32).toString("hex");
next();
});
app.use(
helmet({
contentSecurityPolicy: {
directives: {
scriptSrc: [
"'self'",
(_req, res) => `'nonce-${(res as any).locals.cspNonce}'`,
],
},
},
})
);
app.get("/", (_req, res) => {
res.send(`
<html>
<body>
<script nonce="${res.locals.cspNonce}">
console.log("This script is allowed");
</script>
</body>
</html>
`);
});
3. 特定のヘッダーを無効化する
不要なヘッダーや、アプリの要件と競合するヘッダーは false で無効化できます。
app.use(
helmet({
// CSP は別のミドルウェアで管理する場合など
contentSecurityPolicy: false,
// iframe での埋め込みを許可したい場合
xFrameOptions: false,
})
);
4. 個別のミドルウェアとして使う
helmet の各機能は、スタンドアロンのミドルウェアとしても利用できます。特定のルートにだけ適用したい場合に便利です。
import helmet from "helmet";
// 全体には基本設定を適用
app.use(helmet());
// 特定のルートだけ CSP を上書き
app.get(
"/widget",
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://widget.example.com"],
frameAncestors: ["https://partner-site.com"],
},
}),
(_req, res) => {
res.send("Widget page with custom CSP");
}
);
5. CSP を Report-Only モードで運用する
本番導入前に CSP の影響範囲を確認したい場合、reportOnly: true を使います。ポリシー違反があってもブロックせず、レポートだけを送信します。
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
reportUri: ["/csp-report"],
},
reportOnly: true,
},
})
);
// CSP 違反レポートを受け取るエンドポイント
app.post("/csp-report", express.json({ type: "application/csp-report" }), (req, res) => {
console.warn("CSP Violation:", req.body);
res.sendStatus(204);
});
補足: デフォルトの CSP ディレクティブを取得する
const defaults = helmet.contentSecurityPolicy.getDefaultDirectives();
console.log(defaults);
// {
// "default-src": ["'self'"],
// "base-uri": ["'self'"],
// "font-src": ["'self'", "https:", "data:"],
// ...
// }
類似パッケージとの比較
| パッケージ | 特徴 | Express 対応 | その他のフレームワーク |
|---|---|---|---|
| helmet | オールインワンのセキュリティヘッダー設定。最も広く使われている | ✅ ネイティブ対応 | Connect 互換ミドルウェアなら利用可 |
| csp-header | CSP ヘッダーの生成に特化。ビルダーパターンで記述 | ミドルウェアは別途必要 | フレームワーク非依存 |
fastify-helmet (@fastify/helmet) | Fastify 向けの helmet ラッパー | ❌ | ✅ Fastify 専用 |
| koa-helmet | Koa 向けの helmet ラッパー | ❌ | ✅ Koa 専用 |
手動設定 (res.setHeader) | 完全な制御が可能だが、漏れやすく保守コストが高い | — | — |
Express を使っているなら、helmet が事実上のデファクトスタンダードです。Fastify や Koa を使っている場合は、それぞれ専用のラッパーパッケージを利用してください。
注意点・Tips
⚠️ CSP はアプリに合わせたカスタマイズが必須
デフォルトの CSP は非常に厳格です。Google Fonts、CDN のスクリプト、インラインスタイルなどを使っている場合、そのままではブロックされます。本番投入前に必ず reportOnly モードでテストしてください。
⚠️ Cross-Origin-Embedder-Policy はデフォルトで無効
SharedArrayBuffer などを使う場合に必要な Cross-Origin-Embedder-Policy は、デフォルトでは設定されません。明示的に有効化する必要があります。
app.use(helmet({ crossOriginEmbedderPolicy: true }));
⚠️ Cross-Origin-Resource-Policy が外部からのリソース読み込みをブロックする
デフォルトで same-origin が設定されるため、CDN 経由で画像やフォントを配信している場合、外部サイトから読み込めなくなることがあります。
// CDN として公開する場合
app.use(
helmet({
crossOriginResourcePolicy: { policy: "cross-origin" },
})
);
💡 ミドルウェアの登録順序に注意
helmet はできるだけ早い段階(ルーティングの前)で登録してください。エラーハンドリングのレスポンスにもヘッダーが付与されるようにするためです。
const app = express();
// ✅ 最初に helmet を登録
app.use(helmet());
// その後にルーティングやその他のミドルウェア
app.use(express.json());
app.use("/api", apiRouter);
💡 useDefaults: false でゼロから CSP を構築できる
デフォルトのディレクティブとのマージが不要な場合は、useDefaults: false を指定すると、自分で定義したディレクティブだけが適用されます。
app.use(
helmet({
contentSecurityPolicy: {
useDefaults: false,
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "https://cdn.example.com"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
})
);
まとめ
helmet は Express アプリのセキュリティ対策として、最初に導入すべきミドルウェアです。app.use(helmet()) の一行で 13 種類のセキュリティヘッダーが設定され、XSS・クリックジャッキング・MIME スニッフィングなどの一般的な攻撃リスクを大幅に低減できます。
特に Content-Security-Policy は強力ですがアプリ固有の設定が必要なため、reportOnly モードで段階的に導入するのがベストプラクティスです。週間ダウンロード数が数百万を超えるデファクトスタンダードであり、Express を使うなら入れない理由はありません。