2026年9月10日

2026年9月10日

WordPressにセキュリティヘッダーを追加する方法・XSS・クリックジャッキング対策

はじめに

「セキュリティチェックツールでヘッダーが足りないと指摘された」「XSSやクリックジャッキング攻撃への対策をしたい」——HTTPセキュリティヘッダーを適切に設定することで、ブラウザレベルで多くの攻撃を防御できます。

症状・原因

WordPressはデフォルトでセキュリティヘッダーをほとんど送信しません。https://securityheaders.com/でサイトをチェックすると「F」評価になることが多く、XSS・クリックジャッキング・MIMEスニッフィングなどの攻撃に対して無防備な状態です。

解決手順

ステップ1:現在のヘッダーを確認する

# 現在のレスポンスヘッダーを確認
curl -I https://example.com/ | grep -i \
  -e "x-frame" -e "x-content" -e "strict-transport" \
  -e "content-security" -e "referrer" -e "permissions"

# セキュリティヘッダーのグレードチェック
# https://securityheaders.com/?q=https://example.com/

# SSLの設定もチェック
# https://www.ssllabs.com/ssltest/

ステップ2:Nginxでセキュリティヘッダーを追加する

# /etc/nginx/conf.d/security-headers.conf
server {
    # ...

    # クリックジャッキング防止
    add_header X-Frame-Options "SAMEORIGIN" always;

    # MIMEタイプスニッフィング防止
    add_header X-Content-Type-Options "nosniff" always;

    # XSS保護(古いブラウザ向け)
    add_header X-XSS-Protection "1; mode=block" always;

    # HTTPS強制(HSTS)- 1年間
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # リファラーポリシー
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Permissions Policy(カメラ・マイク等のAPI制限)
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;

    # Content Security Policy(CSP)- 基本設定
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; frame-ancestors 'self';" always;
}
sudo nginx -t && sudo systemctl reload nginx

ステップ3:WordPressのfunctions.phpでヘッダーを追加する

// セキュリティヘッダーをWordPress経由で送信
add_action( 'send_headers', function() {
    // クリックジャッキング防止
    header( 'X-Frame-Options: SAMEORIGIN' );

    // MIMEタイプスニッフィング防止
    header( 'X-Content-Type-Options: nosniff' );

    // HSTS(HSTSはHTTPS環境のみ)
    if ( is_ssl() ) {
        header( 'Strict-Transport-Security: max-age=31536000; includeSubDomains' );
    }

    // リファラーポリシー
    header( 'Referrer-Policy: strict-origin-when-cross-origin' );

    // Permissions Policy
    header( 'Permissions-Policy: camera=(), microphone=(), geolocation=()' );
} );

ステップ4:Content Security Policy(CSP)を設定する

CSPは最も強力ですが、設定が難しいヘッダーです。まずはレポートモードで始めましょう。

// CSPをレポートモードで開始(本番適用前の確認用)
add_action( 'send_headers', function() {
    $csp_report_only = implode( '; ', [
        "default-src 'self'",
        "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com",
        "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
        "font-src 'self' https://fonts.gstatic.com",
        "img-src 'self' data: https:",
        "connect-src 'self' https://www.google-analytics.com",
        "frame-ancestors 'self'",
        "report-uri /wp-json/csp/v1/report", // 違反レポートの受信先
    ] );

    // まずはReport-Onlyで違反を収集
    header( 'Content-Security-Policy-Report-Only: ' . $csp_report_only );
} );

// CSP違反レポートを受信するエンドポイント
add_action( 'rest_api_init', function() {
    register_rest_route( 'csp/v1', '/report', [
        'methods'             => 'POST',
        'callback'            => function( $request ) {
            $body = $request->get_body();
            error_log( 'CSP Violation: ' . $body );
            return new WP_REST_Response( null, 204 );
        },
        'permission_callback' => '__return_true',
    ] );
} );

ステップ5:ヘッダー設定を確認・評価する

# ヘッダーが正しく設定されているか確認
curl -I https://example.com/ 2>/dev/null | \
  grep -iE "x-frame|x-content|strict-transport|content-security|referrer|permissions"

# 期待される出力:
# strict-transport-security: max-age=31536000; includeSubDomains
# x-content-type-options: nosniff
# x-frame-options: SAMEORIGIN
# referrer-policy: strict-origin-when-cross-origin
# permissions-policy: camera=(), microphone=(), geolocation=()

# WordPress REST APIでヘッダーを確認
wp eval '
$response = wp_remote_head(home_url("/"));
$headers = wp_remote_retrieve_headers($response);
foreach (["x-frame-options", "x-content-type-options"] as $h) {
    echo $h . ": " . ($headers[$h] ?? "NOT SET") . "\n";
}'

注意事項

  • Strict-Transport-Security(HSTS)を設定すると、一定期間はHTTPへのアクセスが強制的にHTTPSにリダイレクトされます。SSL証明書に問題が生じた場合にアクセス不能になるため、max-ageは最初は短く(例:3600秒)設定して徐々に延ばしてください。
  • CSPの'unsafe-inline'と'unsafe-eval'は多くのWordPressプラグインで必要ですが、XSS対策の効果が薄れます。Nonce(ワンタイムトークン)を使った厳格なCSPへの段階的移行を検討してください。
  • X-XSS-Protectionは現代のブラウザでは非推奨ですが、古いIEのために残しておくと良いでしょう。

まとめ

セキュリティヘッダーは「現状確認→Nginxヘッダー設定→functions.phpで追加→CSPをReport-Onlyで試験→本番適用」の流れで実装します。securityheaders.comでA評価を目指しましょう。関連記事:WordPressのXMLRPCを無効化する方法、WordPressのログイン試行回数を制限する方法。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲