Security Headers

HTTP セキュリティヘッダーは、現代の Web アプリケーションにおける基本的かつしばしば活用されていない防御層であり、 ブラウザに対して、一般的な多様な攻撃ベクトルに対する ネイティブな保護メカニズムを有効化するよう指示するディレクティブとして機能します。これらの HTTP レスポンスヘッダーは宣言的なセキュリティポリシーとして機能し、 ブラウザがページコンテンツをどのように処理・レンダリングし、スクリプトの実行を制御し、 cookies を管理し、サードパーティリソースとどうやりとりするかについてルールを定めます。適切に設定されると、 Content-Security-Policy (CSP)、Strict-Transport-Security (HSTS)、X-Frame-Options、 X-Content-Type-Options などのヘッダーは、Cross-Site Scripting (XSS)、 clickjacking、MIME sniffing、プロトコルのダウングレード、悪意のあるコードインジェクションなどの攻撃に対して重要な障壁を作り出します。これらのヘッダーを正しく 実装することは、Web アプリケーションの hardening において最も効率的な方法の一つです。 アプリケーションのコード変更を必要とせず、整たるクラスの脆弱性に対して即座に保護を提供するからです。 研究によれば、大多数のサイトは依然としてこれらのヘッダーを適切に実装しておらず、 比較的単純な Web サーバー設定で緩和できたはずの攻撃にユーザーをさらしています。 この怠慢は、自動化されたセキュリティスキャンツールがこれらのヘッダーの存在と適切な設定を アプリケーションのセキュリティ姿勢を示す主要な指標として評価することを考慮すると、特に重大です。

Content-Security-Policy (CSP)

CSP は、scripts、styles、images などのリソースに対して信頼できるソースの whitelist を定義することで XSS を防ぎます。

      # 厳格な CSP(推奨)
      Content-Security-Policy:
      default-src 'self';
      script-src 'self' 'nonce-';
      style-src 'self' 'nonce-';
      img-src 'self' https: data:;
      font-src 'self';
      connect-src 'self' https://api.example.com;
      frame-ancestors 'none';
      base-uri 'self';
      form-action 'self';
      # report-only 付きの CSP(テストモード)
      Content-Security-Policy-Report-Only:
      default-src 'self';
      report-uri /csp-violation-report
      

重要な CSP Directives

  • default-src:他の directives の fallback
  • script-src:JavaScript 実行のための whitelist
  • style-src:許可された CSS ソース
  • frame-ancestors:clickjacking を防止
  • upgrade-insecure-requests:HTTPS を強制

Strict-Transport-Security (HSTS)

HSTS はブラウザに常に HTTPS を使用させ、downgrade attacks や SSL stripping を防ぎます。

      # preload とサブドメイン付きの HSTS
      Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
      # HSTS preload の手順:
      1. preload 付きの HSTS ヘッダーを設定する
      2. hstspreload.org でドメインを送信する
      3. ドメインがブラウザの preload lists に含まれる
      

X-Frame-Options

ページを frame/iframe 内でレンダリングできるかどうかを制御して clickjacking を防ぎます。

      # すべての framing を拒否(最も安全)
      X-Frame-Options: DENY
      # 同一オリジンのみ許可
      X-Frame-Options: SAMEORIGIN
      # 特定のドメインを許可(非推奨 - CSP frame-ancestors を使用)
      X-Frame-Options: ALLOW-FROM https://trusted.com
      

X-Content-Type-Options

ブラウザに宣言された Content-Type を尊重させることで MIME sniffing 攻撃を防ぎます。

      # MIME sniffing を無効化
      X-Content-Type-Options: nosniff
      

Referrer-Policy

requests で送信される referrer information の量を制御します。

      # 推奨ポリシー
      Referrer-Policy: strict-origin-when-cross-origin
      # オプション:
      # - no-referrer:referrer を決して送信しない
      # - no-referrer-when-downgrade:HTTPS→HTTP では referrer を送信しない
      # - same-origin:同一オリジンにのみ送信
      # - strict-origin:origin のみ送信(path なし)
      

Permissions-Policy

ページが使用できる browser features と APIs を制御します(Feature-Policy を置き換えます)。

      # 危険な機能を無効化
      Permissions-Policy:
      geolocation=(),
      microphone=(),
      camera=(),
      payment=(),
      usb=(),
      interest-cohort=()
      

サーバーごとの設定

Nginx

      # nginx.conf
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
      add_header X-Frame-Options "DENY" always;
      add_header X-Content-Type-Options "nosniff" always;
      add_header Referrer-Policy "strict-origin-when-cross-origin" always;
      add_header Content-Security-Policy "default-src 'self';" always;
      add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
      

Apache

      # .htaccess または httpd.conf
      Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
      Header always set X-Frame-Options "DENY"
      Header always set X-Content-Type-Options "nosniff"
      Header always set Referrer-Policy "strict-origin-when-cross-origin"
      Header always set Content-Security-Policy "default-src 'self';"
      

Node.js/Express

      // Helmet middleware を使用
      const helmet = require('helmet');
      app.use(helmet());
      // カスタム設定
      app.use(helmet.contentSecurityPolicy({
      directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'nonce-'"],
      styleSrc: ["'self'", "'nonce-'"],
      }
      }));
      

Security Headers のテスト

  • securityheaders.com:スキャンと A+ 評価
  • Mozilla Observatory:包括的な分析
  • Chrome DevTools:Network タブ → Headers
  • curl:curl -I https://example.com

よくある落とし穴

  • 'unsafe-inline' や 'unsafe-eval' を含む過度に寛容な CSP
  • includeSubDomains のない HSTS はサブドメインを脆弱なままにする
  • ヘッダーが設定されているが適用されていない(always フラグが欠落)
  • ホームページのみをテスト - ヘッダーはすべてのページに存在すべき

完全チェックリスト

  • [OK] 厳格な Content-Security-Policy を設定済み
  • [OK] max-age 1 年 + includeSubDomains の HSTS
  • [OK] X-Frame-Options:DENY または CSP frame-ancestors
  • [OK] X-Content-Type-Options:nosniff
  • [OK] Referrer-Policy を設定済み
  • [OK] 未使用の機能を無効化する Permissions-Policy
  • [OK] securityheaders.com でヘッダーをテスト済み(A+ 評価)
  • [OK] CSP をまず report-only mode でテスト済み