2026年5月20日

2026年5月20日

WordPressのContent Security Policy(CSP)を設定する方法

はじめに

Content Security Policy(CSP)は、ブラウザがどのリソース(スクリプト・スタイル・画像など)を読み込んでよいかを制御するHTTPヘッダーです。XSS(クロスサイトスクリプティング)攻撃を大幅に困難にする強力なセキュリティ機構です。

症状・原因

  • セキュリティ診断でCSPが未設定と指摘された
  • XSS攻撃のリスクを軽減したい
  • Content-Security-Policyヘッダーがなく、ブラウザのセキュリティ機能が活用されていない
  • インラインスクリプトが多用されており、CSP適用に苦労している

解決手順

ステップ1:Report-Onlyモードで現状の違反を把握する

// functions.php: まずはレポートのみモードで安全にテスト
add_action('send_headers', function(): void {
    // ① Report-Only モード: ブロックせず違反をレポートのみ
    // WordPress の標準的なリソースを許可するCSP
    $csp = implode('; ', [
        "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",
        "img-src 'self' data: https:",
        "font-src 'self' https://fonts.gstatic.com",
        "connect-src 'self' https://www.google-analytics.com",
        "frame-src 'self' https://www.youtube.com https://www.google.com",
        "object-src 'none'",
        "base-uri 'self'",
        "report-uri /wp-json/csp/v1/report",
    ]);

    // Report-Only: ブロックせずレポートのみ(安全にテスト)
    header('Content-Security-Policy-Report-Only: ' . $csp);
});
# ブラウザの開発者ツール → コンソールで CSP 違反を確認
# または report-uri.com などのサービスでレポートを収集
# 違反がなくなったら本番 CSP に切り替える

ステップ2:WordPress に適した CSP ディレクティブを設計する

// functions.php: WordPress 環境に合わせたCSPを設定

add_action('send_headers', function(): void {
    // WordPress 標準 + よく使うプラグインに対応したCSP
    $directives = [
        // スクリプト: 自分のサイト + Google系 + インライン許可
        // 理想は nonce を使うが、プラグイン互換性のため unsafe-inline も含める
        "script-src 'self' 'unsafe-inline' 'unsafe-eval'"
            . " https://www.google-analytics.com"
            . " https://www.googletagmanager.com"
            . " https://connect.facebook.net"
            . " https://apis.google.com",

        // スタイル: 自分のサイト + Google Fonts + インライン許可
        "style-src 'self' 'unsafe-inline'"
            . " https://fonts.googleapis.com",

        // 画像: 自分のサイト + データURI + HTTPS全般
        "img-src 'self' data: https: blob:",

        // フォント
        "font-src 'self' https://fonts.gstatic.com data:",

        // 接続先(AJAX, Fetch)
        "connect-src 'self' https://www.google-analytics.com https://stats.g.doubleclick.net",

        // フレーム(YouTube埋め込み等)
        "frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com https://www.google.com",

        // オブジェクト(Flash等): 全て禁止
        "object-src 'none'",

        // フォーム送信先を自分のサイトに制限
        "form-action 'self'",

        // base タグのURL制限
        "base-uri 'self'",

        // デフォルト
        "default-src 'self'",

        // フレームに埋め込まれることを防ぐ
        "frame-ancestors 'self'",
    ];

    $csp = implode('; ', $directives);
    header('Content-Security-Policy: ' . $csp);
});

ステップ3:nonce を使ってインラインスクリプトを安全に許可する

// functions.php: nonce ベースの CSP(より厳格な設定)

// ① CSP nonce を生成して共有
add_action('init', function(): void {
    if (!isset($GLOBALS['csp_nonce'])) {
        $GLOBALS['csp_nonce'] = base64_encode(random_bytes(16));
    }
});

// ② script タグに nonce を付与するフィルター
add_filter('script_loader_tag', function(string $tag, string $handle): string {
    $nonce = $GLOBALS['csp_nonce'] ?? '';
    if ($nonce) {
        $tag = str_replace('<script ', '<script nonce="' . esc_attr($nonce) . '" ', $tag);
    }
    return $tag;
}, 10, 2);

// ③ nonce を使った CSP ヘッダーを送信
add_action('send_headers', function(): void {
    $nonce = $GLOBALS['csp_nonce'] ?? '';
    if (!$nonce) return;

    $csp = "default-src 'self'; script-src 'self' 'nonce-{$nonce}'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; object-src 'none'";
    header('Content-Security-Policy: ' . $csp);
});

ステップ4:CSP違反のレポートエンドポイントを設置する

// functions.php: CSP 違反レポートを受け取るREST APIエンドポイント

add_action('rest_api_init', function(): void {
    register_rest_route('csp/v1', '/report', [
        'methods'             => 'POST',
        'callback'            => function(\WP_REST_Request $request): \WP_REST_Response {
            $body = $request->get_body();
            $report = json_decode($body, true);

            // ログに記録
            error_log('CSP Violation: ' . wp_json_encode($report));

            // 必要に応じてDBに保存やメール通知
            return new \WP_REST_Response(['status' => 'received'], 200);
        },
        'permission_callback' => '__return_true',
    ]);
});

ステップ5:設定を確認する

# CSP ヘッダーが送信されているか確認
curl -sI https://example.com/ | grep -i "content-security-policy"

# ブラウザで確認(開発者ツール → Network → Response Headers)
# CSP 違反がコンソールに表示されるか確認

# オンラインツールで CSP を解析
# https://csp-evaluator.withgoogle.com/ で脆弱なCSPを検出

# WP-CLI で確認
wp eval "
\$r = wp_remote_head('https://example.com/');
echo wp_remote_retrieve_header(\$r, 'content-security-policy');
"

注意事項

  • WordPress は多くのプラグイン・テーマがインラインスクリプトを使用するため、'unsafe-inline'を完全に排除するのは難しい場合があります。まずReport-Onlyモードでテストし、違反を把握してから段階的に厳格化してください
  • Google Analytics・Google Tag Manager・Facebook Pixel などのサードパーティスクリプトはscript-srcに各ドメインを追加する必要があります。CDNを使用している場合はCDNのドメインも必要です
  • CSPを誤って設定するとサイトの機能が壊れます。必ずContent-Security-Policy-Report-Onlyでテストしてから本番適用してください

まとめ

CSP設定は①Content-Security-Policy-Report-Onlyヘッダーで違反を把握、②WordPress標準リソース(Google Analytics・YouTube等)をscript-srcstyle-srcに列挙、③nonceを使いインライン許可を最小化、④/wp-json/csp/v1/reportエンドポイントで違反ログを収集、⑤curl -sIでヘッダー送信を確認・オンラインツールで評価します。

お気軽にご相談ください

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