2026年5月20日
2026年5月20日
WordPressのXSS(クロスサイトスクリプティング)対策をする方法
はじめに
XSS(クロスサイトスクリプティング)は、攻撃者が悪意のあるスクリプトをWebページに埋め込み、訪問者のブラウザ上で実行させる攻撃です。WordPressでは適切な出力エスケープ・入力検証・CSPヘッダーを組み合わせることで防御できます。
症状・原因
- コメントやフォームに
を入力したらダイアログが表示された - セキュリティ診断でXSS脆弱性が指摘された
- プラグインやテーマのカスタマイズで
echo $_GET['param']のような安全でない出力をしている - ユーザー入力をエスケープせずにHTMLに出力している
解決手順
ステップ1:XSS の脆弱なパターンを検出する
# 安全でない出力パターンを検索
grep -rn "echo \$_GET\|echo \$_POST\|echo \$_REQUEST\|echo \$_COOKIE" /var/www/html/wp-content/
# エスケープなしの変数出力を検索
grep -rn "echo \$[a-zA-Z]" /var/www/html/wp-content/themes/
grep -rn "echo \$[a-zA-Z]" /var/www/html/wp-content/plugins/
# esc_ 関数を使っていない箇所を特定(WP-CLI 使用)
wp eval-file /dev/stdin << 'EOF'
// 現在のテーマの PHP ファイルをスキャン
$theme_dir = get_template_directory();
$files = glob($theme_dir . '/**/*.php');
foreach ($files as $file) {
$content = file_get_contents($file);
if (preg_match('/echo\s+\$(?!.*esc_)/m', $content)) {
echo basename($file) . ': 要確認' . PHP_EOL;
}
}
EOF
ステップ2:WordPress の出力エスケープ関数を使う
// functions.php / テーマファイル: 適切なエスケープ関数の使い方
// ① HTML テキストのエスケープ(最も一般的)
// 悪い例
echo $user_input;
echo get_query_var('search');
// 良い例
echo esc_html($user_input);
echo esc_html(get_query_var('search'));
// ② HTML 属性値のエスケープ
// 悪い例
echo '<input value="' . $value . '">';
// 良い例
echo '<input value="' . esc_attr($value) . '">';
// ③ URL のエスケープ
// 悪い例
echo '<a href="' . $url . '">';
// 良い例
echo '<a href="' . esc_url($url) . '">';
// ④ JavaScript 内の出力
// 悪い例
echo '<script>var name = "' . $name . '";</script>';
// 良い例
echo '<script>var name = ' . wp_json_encode($name) . ';</script>';
// ⑤ SQL クエリのエスケープ(XSSではなくSQLi対策だが重要)
global $wpdb;
// 悪い例
$wpdb->query("SELECT * FROM wp_posts WHERE ID = " . $_GET['id']);
// 良い例
$wpdb->get_row($wpdb->prepare("SELECT * FROM wp_posts WHERE ID = %d", $_GET['id']));
ステップ3:wp_kses で許可するHTMLタグを制限する
// functions.php: wp_kses でユーザー入力のHTMLを安全にフィルタリング
// ① コメントや投稿本文に許可するタグを定義
$allowed_html = [
'a' => ['href' => [], 'title' => [], 'target' => ['_blank']],
'br' => [],
'em' => [],
'strong' => [],
'p' => ['class' => []],
'ul' => [], 'ol' => [], 'li' => [],
'blockquote' => ['cite' => []],
'code' => [],
];
// ② ユーザー入力を安全にフィルタリング
$safe_content = wp_kses($user_content, $allowed_html);
echo $safe_content;
// ③ 完全にテキストのみ許可する場合
$safe_text = wp_strip_all_tags($user_content);
echo esc_html($safe_text);
// ④ フォーム送信時の nonce 検証(CSRF + XSS の複合対策)
// フォームに nonce を追加
wp_nonce_field('my_action', 'my_nonce');
// 送信時に検証
if (!isset($_POST['my_nonce']) || !wp_verify_nonce($_POST['my_nonce'], 'my_action')) {
wp_die('不正なリクエストです。');
}
ステップ4:コメントのXSS対策を強化する
// functions.php: コメントの XSS 対策
// ① コメント内のHTMLを制限(WordPress デフォルトより厳格に)
add_filter('pre_comment_content', function(string $content): string {
// スクリプトタグを完全に除去
$content = wp_strip_all_tags($content, false);
// または wp_kses で許可タグを制限
return $content;
});
// ② コメント欄でのHTMLを完全無効化
add_filter('comment_text', function(string $comment_text): string {
// リンクのみ許可してそれ以外のHTMLを除去
$allowed = ['a' => ['href' => [], 'rel' => []]];
return wp_kses($comment_text, $allowed);
});
// ③ Stored XSS: 投稿保存時にサニタイズ
add_filter('content_save_pre', function(string $content): string {
// 管理者・編集者以外はスクリプトタグを除去
if (!current_user_can('unfiltered_html')) {
$content = wp_kses_post($content);
}
return $content;
});
ステップ5:CSP ヘッダーと X-XSS-Protection で二重防御する
// functions.php: ヘッダーベースのXSS防御
add_action('send_headers', function(): void {
// ① X-XSS-Protection(旧ブラウザ向け)
header('X-XSS-Protection: 1; mode=block');
// ② CSP で インラインスクリプトを制限(最も強力)
// まず Report-Only でテスト
$csp = "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'";
header('Content-Security-Policy: ' . $csp);
// ③ X-Content-Type-Options: MIME スニッフィングを防ぐ
header('X-Content-Type-Options: nosniff');
});
# 設定後の確認
curl -sI https://example.com/ | grep -iE "x-xss|content-security|x-content-type"
# XSS テスト(開発環境のみ)
curl -s "https://dev.example.com/?s=<script>alert(1)</script>" | grep -i "script"
# エスケープされていれば <script> と表示される
注意事項
esc_html()は<>を<>に変換するため、HTMLタグを含む内容には使えません。HTMLタグを許可する場合はwp_kses()またはwp_kses_post()を使用してくださいunfiltered_html権限を持つ管理者・編集者はスクリプトタグを含む投稿が可能です。これはWordPressの仕様ですが、信頼できないユーザーに編集者権限を与えないよう注意してくださいwp_json_encode()はJavaScriptに変数を渡す際に使用します。json_encode()とは異なりUnicode文字をエスケープします
まとめ
XSS対策は①grep -rn "echo \$_GET"で脆弱なパターンを検出、②esc_html()・esc_attr()・esc_url()・wp_json_encode()を用途に応じて使い分け、③wp_kses()で許可するHTMLタグを制限、④フォームにはwp_nonce_field()でCSRFトークンを追加、⑤X-XSS-ProtectionとCSPヘッダーで二重防御します。