sanitize_text_fieldとwp_ksesの使い分け完全ガイド|WordPress入力無害化とXSS対策

WordPress開発におけるセキュリティ原則の最も基本となる合言葉が「Sanitize on Input, Escape on Output(入力時に無害化し、出力時にエスケープする)」です。

しかし、実際の開発現場では「とりあえず sanitize_text_field() を使っておけば安全」と考え、複数行テキストの改行が消えてしまったり、リッチテキストのHTMLタグが全削除されてしまったり、あるいは出力時エスケープを怠ってXSS(クロスサイトスクリプティング)脆弱性を作り出してしまう事例が非常に多く見られます。

💡 本記事は「WordPress セキュアコーディング実践シリーズ」の第4弾です

本記事では、WordPressの代表的なサニタイズ関数 sanitize_text_field()wp_kses() / wp_kses_post() の動作の違い、適切な使い分け、その他の特化サニタイズ関数、そして出力時エスケープ関数との正しい連携パターンを徹底解説します。


1. サニタイズ(入力無害化)とエスケープ(出力無害化)の違い

WordPressにおけるデータセキュリティは、「2段階の防御」によって成立しています。

段階用語実行タイミング主な目的代表的関数
第1段階サニタイズ(Sanitizing)データベース保存直前(POST受信時)不正な文字・意図しないタグ・危険なオクテットの除去、型の正規化sanitize_text_field(), wp_kses_post()
第2段階エスケープ(Escaping)HTML/属性/JS出力直前(Late Escaping)ブラウザでの不正スクリプト実行(XSS)の防止esc_html(), esc_attr(), esc_url()

「DBに保存する時にサニタイズしたから、出力時は生で echo してOK」という考えは重大なアンチパターンです。DB内のデータが常に安全とは限らない(管理画面直接操作や別処理の混入)ため、出力直前のエスケープ(Late Escaping)は絶対に省略してはなりません


2. sanitize_text_field() の動作仕様と使いどころ

sanitize_text_field( string $str ) は、ユーザー入力から「プレーンな1行文字列」を抽出するための最も標準的な関数です。

sanitize_text_field() が実行する処理

  • 無効なUTF-8文字のチェックと除去
  • すべてのHTMLタグ(<script>, <b>, <a> 等)の完全削除(strip_tags相当)
  • 改行コード(\r, \n)およびタブ(\t)の除去
  • 連続する半角空白の圧縮・前後の余白トリム
  • オクテット(URLエンコードされた文字)の安全化

適切なユースケース vs 不適切なユースケース

用途区分入力項目例使用可否と推奨関数
適切 ⭕氏名、ユーザー名、電話番号、郵便番号、単一行のタイトル・見出し、短い設定値sanitize_text_field()
不適切 ❌問い合わせフォームの本文、備考欄、複数行のメッセージsanitize_textarea_field()(改行を保持)
不適切 ❌ブログ本文、HTML装飾を含むリッチテキスト、埋め込みコードwp_kses_post() / wp_kses()
不適切 ❌メールアドレス、URL、ファイル名専用の sanitize_email(), esc_url_raw()

3. wp_kses() と wp_kses_post() による安全なHTML許可制御

ユーザーや管理者にHTMLタグ(リンク、太字、リスト、画像など)の入力を許可したい場合、sanitize_text_field() を使うとすべてのタグが消えてしまいます。そこで使用するのが KSES(HTML無害化ライブラリ) です。

① wp_kses_post():一般的な投稿本文向け

WordPressの通常投稿本文で許可されている標準HTMLタグ(<p>, <a>, <img>, <strong>, <ul> 等)をすべて安全に通し、悪意のある <script>onload などの危険な属性・イベントハンドラだけを除去します。

// リッチテキストエディタの入力を保存する場合
$safe_html = wp_kses_post($_POST['rich_description']);
update_post_meta($post_id, '_custom_rich_desc', $safe_html);

② wp_kses():許可タグを明示的に指定する場合

「リンク(<a>)と太字(<strong>)だけを許可し、画像やテーブルは禁止したい」など、厳格なホワイトリスト制御を行う場合は wp_kses( $string, $allowed_html ) を使用します。

<?php
// 許可するタグと属性のホワイトリスト配列を定義
$allowed_html = array(
    'a' => array(
        'href'   => array(),
        'title'  => array(),
        'target' => array(),
        'rel'    => array(),
    ),
    'strong' => array(),
    'em'     => array(),
    'span'   => array(
        'class' => array(),
    ),
);

$user_bio = wp_kses($_POST['user_bio_html'], $allowed_html);
update_user_meta($user_id, 'description_html', $user_bio);

4. WordPress標準サニタイズ関数チートシート

データ種別サニタイズ関数主な動作・特徴
1行テキストsanitize_text_field()タグ除去・改行除去・空白トリム
複数行テキストsanitize_textarea_field()タグ除去・改行(\n)は保持
HTML投稿wp_kses_post()投稿本文標準タグのみ許可
制限付きHTMLwp_kses()指定ホワイトリストタグのみ許可
メールアドレスsanitize_email()メール形式に不正な文字を除去
ファイル名sanitize_file_name()無効文字・パス移動(../)を除去
URL(DB保存用)esc_url_raw() / sanitize_url()DB保存用の生URL無害化
キー・スラッグsanitize_key() / sanitize_title()小文字・英数字・ハイフンに正規化
CSSクラス名sanitize_html_class()有効なHTMLクラス名に限定
整数値・IDabsint() / intval()正の整数(ID)にキャスト

5. 出力時エスケープ関数との連携(Late Escapingの実践)

画面にデータを出力する際は、コンテキスト(HTML本文、HTML属性、URL、JavaScript内)に合わせて適切なエスケープ関数を選択します。

<!-- 1. 通常のテキスト出力 -->
<p><?php echo esc_html( $user_name ); ?></p>

<!-- 2. HTML属性内(value, title, placeholder等)への出力 -->
<input type="text" name="title" value="<?php echo esc_attr( $post_title ); ?>">

<!-- 3. リンク先URL(href, src等)への出力 -->
<a href="<?php echo esc_url( $external_link ); ?>">リンク</a>

<!-- 4. JavaScript変数への埋め込み -->
<script>
    const config = <?php echo wp_json_encode( $server_data ); ?>;
</script>

6. 実践コード:受信・サニタイズ・保存・出力の完全フロー

これまでのセキュアコーディングシリーズ(nonce検証・権限チェック・サニタイズ・エスケープ)を統合した完全な実装例です。

<?php
// フォーム保存ハンドラ
add_action('admin_post_save_company_profile', 'handle_save_company_profile');

function handle_save_company_profile() {
    // 1. CSRF対策(nonce検証)
    check_admin_referer('company_profile_action', 'company_profile_nonce');

    // 2. 認可チェック(権限確認)
    if ( ! current_user_can('manage_options') ) {
        wp_die('アクセス権限がありません。', 403);
    }

    // 3. 入力値の適切なサニタイズ
    $company_name = isset($_POST['company_name']) ? sanitize_text_field($_POST['company_name']) : '';
    $company_url  = isset($_POST['company_url']) ? esc_url_raw($_POST['company_url']) : '';
    $description  = isset($_POST['description']) ? wp_kses_post($_POST['description']) : '';
    $employee_num = isset($_POST['employees']) ? absint($_POST['employees']) : 0;

    // 4. DB保存
    update_option('my_company_name', $company_name);
    update_option('my_company_url', $company_url);
    update_option('my_company_desc', $description);
    update_option('my_company_employees', $employee_num);

    wp_safe_redirect(add_query_arg('updated', '1', wp_get_referer()));
    exit;
}

7. まとめ&セキュアコーディングシリーズ関連記事

サニタイズとエスケープは、WordPressにおけるXSS脆弱性やSQLインジェクション、データ破壊を防ぐための最も重要かつ日常的なコーディング習慣です。

「1行テキストなら sanitize_text_field()」「リッチHTMLなら wp_kses_post() / wp_kses()」「出力時は必ず esc_html() / esc_attr()の使い分けを徹底し、安全性の高いWordPressプラグイン・テーマ開発を実践しましょう。

📚 開発者向けセキュアコーディング関連記事

🛡️ サイトに潜むXSSやセキュリティリスクを無料診断!

エスケープ漏れやプラグインの脆弱性は、サイトの改ざんや情報漏えいに直結します。「MozCheck」は、WordPressサイトのURLを入力するだけで、セキュリティ状態を数分でレポートする無料オンライン診断ツールです。

投稿者

🧰 WordPress無料診断

サイト改善の第一歩をお届け

当サイト「MozCheck」は、WordPressサイトの不安をチェックできる無料診断サービスです。
URLを入力するだけ。登録不要、すぐに診断結果が表示されます。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です