万が一、当サイトで重大な問題を発見した際などは、フォーラムや WordSlack #docs チャンネルでお知らせください。</p>
データ検証
このページは最新情報に追随して更新されていません。英語版のプラグインハンドブックをご確認ください。翻訳にご協力くださる方はぜひご相談ください。
信頼できないデータが、さまざまな情報源から入ってきます (ユーザー、第三者のサイト、あなた自身のデータベースも!…)。そして、これらすべては、入力時と出力時の両方で検証する必要があります。
目次
[非表示]出力の無害化
データの無害化 (サニタイズ) をする方法は、データの種類およびそれが使われる文脈 (コンテキスト) に依存します。WordPress における共通の処理と、どのように無害化すべきかを以下に示します。
ヒント : 出力の検証は、可能な限り遅く行うことがよいでしょう。スクリプトでは、出力をする、まさにその時に検証するのが理想です。こうすることで、常にデータが適切に検証/エスケープされていることが確認でき、変数が既に検証されているか覚えておく必要はありません。
整数値
- intval( $int ) または (int) $int
- 整数値を想定するならば、キャストします。
- absint( $int )
- 結果が負でないことを保証します。
HTML/XML
(HTML 書類とは異なり) 多くの形式の XML 書類が理解する文字実体参照は '
、&
、>
、<
、"
の 5 つのみであることに注意してください。テキストを XML 書類として出力するときは、不正な文字実体参照を含むあらゆるテキストを、WordPress の ent2ncr( $text )
/en 関数を通してフィルターしてください。
HTML/XML の断片
- wp_kses( (string) $fragment, (array) $allowed_html, (array) $protocols = null )
- KSES は悪意あるスクリプトを除去します。すべての信頼できない HTML (投稿文、コメント文など) は
wp_kses()
を通すべきです。使い方やデフォルト値などはwp-includes/kses.php
を参照してください。 - 許可する HTML タグの配列を
wp_kses()
へ渡す代わりにwp_kses_post( (string) $fragment )
を用いれば、投稿や固定ページで許可されるタグのみを残せます。またwp_kses_data( (string) $fragment )
を用いれば、コメントで許可される少数のタグのみを残せます。 - KSES システムはリソースをたくさん消費する場合があるので、出力を無害化するフィルターとして直接実行するのは避けるべきであることに注意してください。そうではなくデータが入力され処理された後、データベースへ保存する前にフィルターとすべきです。例えば WordPress は KSES を pre_comment_content フィルターとして実行し、コメントを保存する前に HTML を除去します。
-
wp_rel_nofollow( (string) $html )
/en - あらゆる <a> リンクに "rel='nofollow'" 属性をつけます。
-
wp_kses_allowed_html( (string) $context )
- 所定のコンテキストで許可される HTML タグの配列を提供します。指定できるのは post, strip, data, entities または pre_user_description のようなフィールド用フィルターの名前です。
テキスト節
-
esc_html( $text )/en
(Version 2.8 以降) - 「< (小ナリ)」、「> (大ナリ)」、「& (アンド)」、「" (ダブルクォーテーション)」、「' (シングルクォーテーション)」 をエンコードします。
esc_attr
と同様ですが、こちらは出力にesc_html
フィルターを適用します。 -
esc_html__ /en
(Version 2.8 以降) - 翻訳、エンコードをします。
-
esc_html_e /en
(Version 2.8 以降) - 翻訳、エンコード、出力をします。
-
esc_textarea /en
(Version 3.1 以降) - textarea 要素の中で使用するためにテキストをエンコードします。
-
sanitize_text_field
(Version 2.9.0 以降) - ユーザーからの入力またはデータベースからの入力を無害化します。
属性値
-
esc_attr( $text )
(Version 2.8 以降) - 「< (小ナリ)」、「> (大ナリ)」、「& (アンド)」、「" (ダブルクォーテーション)」、「' (シングルクォーテーション)」 をエンコードします。
esc_html
と同様ですが、こちらは出力にattribute_escape
フィルターを適用します。 -
esc_attr__()
- 翻訳、エンコードをします。
-
esc_attr_e()
- 翻訳、エンコード、出力をします。
JavaScript
URL
-
esc_url( $url, (array) $protocols = null )
(Version 2.8 以降) - URL を無害化するときは、常に
esc_url
を使ってください (テキスト中、属性値、その他あらゆる場所で)。ホワイトリストで提供されたプロトコル(デフォルトは http ならびに https、ftp、ftps、mailto、news、irc、gopher、nntp、feed、telnet)を持たない URL は拒否し、不正な文字を排除し、危険な文字を除去します。3.0 で非推奨とされたclean_url()
の代わりに使います。 - この関数は文字を HTML 実体にエンコードします。(X)HTML あるいは XML 書類を生成するときはこの関数を使ってください。アンド (&) とシングルクォート (') は数値実体参照 (&, ') にエンコードします。
-
esc_url_raw( $url, (array) $protocols = null )
/en (Version 2.8 以降) - URL をデータベースに格納するときに使う関数です。この関数は文字を HTML 実体にエンコードしません。URL を保存するときやエンコードしない形で保存したいときはこの関数を使ってください。この機能は古い関数
clean_url
関数の$context
をdb
に設定したときと同等です。 -
urlencode( $scalar ) /en
- URL をエンコードします。(例えばクエリパラメータで使用するなど)
-
urlencode_deep( $array ) /en
- 配列のすべての要素を URL エンコードします。
データベース
-
$wpdb->insert( $table, (array) $data )
-
$data
は未エスケープとしてください (この関数がエスケープしてくれます)。配列キーがカラム、配列値がデータベース値になります。 -
$wpdb->update( $table, (array) $data, (array) $where )
-
$data
は未エスケープとしてください。配列キーがカラム、配列値がデータベース値になります。$where
は未エスケープとしてください。複数のWHERE
節はAND
で連結されます。
$wpdb->update( 'my_table', array( 'status' => $untrusted_status, 'title' => $untrusted_title ), array( 'id' => 123 ) );
-
$wpdb->prepare( $format, (scalar) $value1, (scalar) $value2, ... )
-
$format
は sprintf() 形式に似た文字列です。%s
、%d
、%f
のみ理解します。どれもクォート文字で囲む必要はありません。
$wpdb->get_var( $wpdb->prepare( "SELECT something FROM table WHERE foo = %s and status = %d", $name, // 未エスケープの文字列 (関数が無害化します) $status // 信頼できない整数値 (関数が無害化します) ) );
-
esc_sql( $sql )
- 文字列またはその配列を SQL クエリで使えるようにエスケープします。配列に対して動作する、見せかけの
addslashes()
です。一般的に$wpdb->prepare()<code> の方が好まれます。これは共通のフォーマットエラーを直してくれるからです。
-
<code>$wpdb->escape( $text )</code> - 3.6から非推奨になりました。代わりに esc_sql() または $wpdb->prepare() を使ってください。
<code>$wpdb->escape_by_ref( &$text )
- 返り値はありません。パラメータが参照で渡されるので、テキストが直接変更され、返り値を割り当てる必要がありません。
-
$wpdb->esc_like( $text )
/en -
$text
を SQL クエリの LIKE 式で使えるように無害化します。別途 SQL エスケープが必要です(上記のいずれかの関数を使って)。 -
like_escape( $string )
- 4.0 から非推奨になりました。$wpdb->esc_like() を代わりに使ってください。
ファイルシステム
-
validate_file( (string) $filename, (array) $allowed_files = "" )
/en - ディレクトリートラバーサル攻撃を防止するため、あるいはファイル名がホワイトリストにあるか確認するために使用します。
$filename
が正当な相対パスなら 0 を返します。検証後、$filename
を相対パスとして扱わなければなりません (MUST) (例えば、ABSPATH の後に繋げる等)。なぜなら、/etc/hosts
のような文字列はこの関数で正当と判定されるからです。返り値がゼロより大きい整数の場合は、.., ./, あるいは : がパスに含まれているか、または$allowed_files
ホワイトリストに含まれていません。この結果を真偽判定するときは次の点に注意してください。false (0) はファイル名が検証を通過したことを示し、true (> 0) は通過しなかったことを示します。
HTTP ヘッダー
ヘッダー分割攻撃は HTTP クライアントに依存するため、やっかいなものです。WordPress は HTTP ヘッダーにユーザーが生成したコンテンツを含める必要はほとんどありません。しかしそうするなら、WordPress は HTTP ヘッダーの多くにホワイトリストを用います。
WordPress は、HTTP の Location ヘッダーにユーザーが生成したコンテンツを使えますが、以下のように無害化できます。
-
wp_redirect($location, $status = 302)
- あらゆる URL に対する安全なリダイレクト方法です。結果の HTTP Location ヘッダーが妥当であることを保証します。
-
wp_safe_redirect($location, $status = 302)
- さらに安全です。ホワイトリストにあるドメインのみへリダイレクトします。
入力の検証
出力の無害化に挙げられた多くの関数は入力の検証にも使えます。さらに WordPress は以下の関数を使っています。
スラッグ
-
sanitize_title( $title )
/en - 投稿スラッグなどに使われています。
-
sanitize_user( $username, $strict = false )
/en - 新規ユーザーを作成するときは
$strict
を使ってください (特に API でユーザーを追加するとき)。
HTML
-
balanceTags( $html )
/en またはforce_balance_tags( $html )
/en - 正当な XML 出力になるよう、HTML タグの開き/閉じの釣り合いを取ります。
-
tag_escape( $html_tag_name )
/en - HTML タグ名を無害化します (関数名の印象とは違って、何もエスケープしません)。
-
sanitize_html_class( $class, $fallback )
/en - HTML クラス名を無害化し、妥当な文字列のみを含むことを保証します。A-Z,a-z,0-9,'-' に限定します。結果が空文字列になる場合は、第 2 引数で与えられた代替文字列を返します。
配列
-
array_map( 'absint', $array )
- 配列要素がすべて非負であることを保証します。あなたのデータに合うようにコールバック関数 'absint' を入れ替えてください。array_map() は PHP コアの関数 で、配列の要素に任意のコールバック関数を実行します。この例ではコールバック関数は absint() です。
その他
その他の、データの無害化に役立つ関数です。
- sanitize_email() /en
- sanitize_file_name() /en
- sanitize_html_class() /en
- sanitize_key() /en
- sanitize_mime_type() /en
- sanitize_option() /en
- sanitize_sql_orderby() /en
- sanitize_text_field()
- sanitize_title_for_query() /en
- sanitize_title_with_dashes() /en
- sanitize_user() /en
- sanitize_meta() /en
- sanitize_term()
- sanitize_term_field()
検証の哲学
検証をどのように行うか、いくつか異なった哲学があります。どれが正しいかは筋書によって異なります。
ホワイトリスト
既知および信頼された値の有限な一覧にあるデータのみ受理します。
ホワイトリストに対して信頼されていないデータを比較するとき、厳密な型チェックが行われていることを確認することが重要です。そうでなければ攻撃者が入力を工夫して、ホワイトリストを通過するけれどもまだ悪質な効果を持たせることができるかもしれません。
比較演算子
$untrusted_input = '1 malicious string'; // ゆるく比較すると値が 1 になる
if ( 1 === $untrusted_input ) { // == を使うと true になるが、=== なら false
echo '<p>有効なデータ';
} else {
wp_die( '不正なデータ' );
}
in_array()
$untrusted_input = '1 malicious string'; // ゆるく比較すると値が 1 になる
$safe_values = array( 1, 5, 7 );
if ( in_array( $untrusted_input, $safe_values, true ) ) { // `true` は厳密な型チェックを有効にする
echo '<p>有効なデータ';
} else {
wp_die( '不正なデータ' );
}
switch()
$untrusted_input = '1 malicious string'; // ゆるく比較すると値が 1 になる
switch ( true ) {
case 1 === $untrusted_input: // switch() のゆるい比較に頼るのでなく自分で厳密に比較する
echo '<p>有効なデータ';
break;
default:
wp_die( '不正なデータ' );
}
ブラックリスト
既知の信頼できない値の有限な一覧にあるデータを拒否します。これがよい方法であることは、めったにありません。
書式の検出
データが正しい書式であるかテストします。正しい場合のみ受理します。
if ( ! ctype_alnum( $data ) ) { wp_die( "無効な形式です。あなたのデータは※△□%&〒☆" ); } if ( preg_match( "/[^0-9.-]/", $data ) ) { wp_die( "無効な形式です。浮動小数じゃない? おバカ!" ); }
書式の訂正
ほとんどのデータを受理し、危険な部分を除去または変更します。
$trusted_integer = (int) $untrusted_integer; $trusted_alpha = preg_replace( '/[^a-z]/i', "", $untrusted_alpha ); $trusted_slug = sanitize_title( $untrusted_slug );
変更履歴
- 3.6:
$wpdb->escape()
の代わりにesc_sql()
と$wpdb->preapre()
が推奨となりました。 - 3.1:
esc_textarea
が導入されました。 (#15454) - 3.0:
clean_url()
の代わりにesc_url()
とesc_url_raw()
が推奨となりました。 (#12309) - 2.8: 以下の関数が非推奨となりました。(WordPress Development Updates を参照)
-
sanitize_url()
->esc_url_raw()
-
wp_specialchars()
->esc_html()
(他にesc_html__()
およびesc_html_e()
) -
attribute_escape()
->esc_attr()
(他にesc_attr__()
およびesc_attr_e()
)
-
外部資料
- Data Sanitization and Validation With WordPress by Stephen Harris
- Theme and Plugin Security by Mark Jaquith
- wp_specialchars() vs attribute_escape() ( now esc_attr() ) and quote entity-encoding.
最新英語版: WordPress Codex » Data Validation (最新版との差分)