セキュリティ プログラミング ソフトウェア 公開日 2026.05.20 更新日 2026.06.13

サニタイズとは?エスケープ・バリデーションとの違いと実務での使い分け

サニタイズは 「入力や出力から危険な要素を取り除いて無害化する処理」 ですが、エスケープやバリデーションと混同されがちです。XSS / SQL インジェクション / コマンドインジェクション / Prompt Injection といった代表的な攻撃に対し、「どこで何をやるか」 を間違えると簡単に事故ります。3者の違いと、実務でどう向き合うかを整理します。

先に要点

  • サニタイズは 入力や出力から危険な要素を取り除き、無害な形に変換する処理。「削る / 置換する / 無効化する」 が中心で、「そもそも受け取らない」 のはバリデーションの仕事。
  • サニタイズ / エスケープ / バリデーション は役割が違う。「バリデーション = 入口で拒否」、「サニタイズ = 取り除く / 加工する」、「エスケープ = 出力先の文法に合わせて変換」 と覚えると整理しやすい。
  • XSS には 出力時のエスケープ + HTML 入力時のサニタイズSQL インジェクションには プレースホルダ(prepared statement)、コマンドインジェクションには シェルを介さない API が基本。「サニタイズだけ」 で全部塞ごうとすると必ず漏れる。
  • ブラックリスト方式のサニタイズ(「危ない文字列を消す」)は、エンコーディング・パーサーの差・新しい攻撃パターンで簡単に迂回される。ホワイトリスト + 信頼境界での処理が原則。
  • AI 時代の プロンプトインジェクション も 「サニタイズで完全に防ぐ」 は不可能。「分離 / 監視 / 権限最小化」 を併用するのが現実解。

PHP に htmlspecialchars があるから XSS は大丈夫」 「入力をエスケープしてるから SQL インジェクション は防げてる」 ── 実は、こういう言い回しは 用語が混ざっていて危険サイン です。

サニタイズ・エスケープ・バリデーションは 似ているけれど別の処理 で、「どこで」 「何のために」 やるかを間違えると、どれだけ熱心にコードを書いても穴が残ります。

この記事では、3者の違いをまず整理してから、XSS / SQL インジェクション / コマンドインジェクション / プロンプトインジェクション の代表的な攻撃面に対する 実務での向き合い方 を見ていきます。

サニタイズとは — まず一言で言うと

サニタイズ(sanitization、サニタイゼーション)は、入力や出力に含まれる危険な要素 / 不要な要素を取り除いて、無害な形にする処理 です。日本語では 無害化 と訳されることが多く、情報処理推進機構(IPA)の解説でもこの語が使われます。

何を 「危険」 とみなすかは 処理する場所と用途によって変わります

HTML を扱うフォームでのサニタイズ

ユーザーがリッチテキストで投稿してきた HTML から、「<script> タグ」 「<iframe>」 「onerror などのイベントハンドラ属性」 を 除去 する。「許可するタグ / 属性」 を決め、それ以外は捨てる。

ファイル名のサニタイズ

アップロード時に 「../」 や 「\」 などのパス区切りや、制御文字を 除去 / 置換 して、「同じディレクトリに正規化したファイル名で保存する」 形にする。

ログのサニタイズ

個人情報」 や 「クレジットカード番号」 をログに書き出す直前に マスキング する。「これもサニタイズの一種」 と捉えると、用途の広さがイメージしやすい。

AI に渡すプロンプトのサニタイズ

「 業務データを LLM に入れる前に、機密情報 / 個人情報伏字に変換する」 のもサニタイズの仲間。AI 入力の注意点 の現場で実際に使われる。

つまりサニタイズは 「 ある場所での危険性を、その場所で削って整える」 処理 と理解するのが正確です。「入力時にやる」 ものとも限らないし、「出力時にやる」 ものとも限らない、というのがポイントです。

サニタイズ / エスケープ / バリデーションの違い

3者は 役割と実行する場所が違う ので、まず表で整理します。

処理 目的 典型的な場所 結果 代表例
バリデーション 仕様に合わない入力を拒否する 入口(コントローラ / フォーム) 合格 or エラーで弾く 「 メールアドレス形式チェック」 「数値範囲チェック」 「Zod での型検証」
サニタイズ 危険要素を削除 / 置換して残りを使えるようにする 入口 or 保存前 or 出力前 無害化された値 「 HTML タグの許可リストフィルタ」 「制御文字の除去」
エスケープ 出力先の文法的な意味を奪う 出力直前(テンプレ / クエリ) 文法的に安全な文字列 「htmlspecialchars」 「テンプレートエンジンの自動エスケープ」 「プレースホルダ」

ざっくり方向性で言うとこうなります。

バリデーションは 「入れる / 入れない」 の判定

「 想定した形」 でない入力は そもそも受け取らない。たとえば年齢に 「abc」 が来たら 400 で返す。サニタイズと違って、入力を加工せず弾くのが基本姿勢。

サニタイズは 「削る / 整える」 の加工

「 受け入れて使う前提だが、危険な部分は除く」。リッチテキスト投稿のように 「捨てるとサービスとして成立しないもの」 を扱うときに必要になる。

エスケープは 「相手の言語に翻訳」

「 HTML として出力するなら HTML の文法で安全に」 「SQL なら SQL の文法で安全に」 「シェルならシェルの文法で安全に」。元の値を変えるのではなく、出力する文脈に合わせて表現を変える

3つは相補的

「 バリデーション + サニタイズ + エスケープ」 を併用するのが標準。「どれか1つで全部塞ぐ」 は無理。重要なのは どこで何をするか を意識して設計すること。

PHP で htmlspecialchars($_POST[「name」])」 してから DB に保存してる、これでサニタイズ済みです」 のような実装は、用途が間違っています。「htmlspecialchars」 は HTML 出力用のエスケープ なので、DB に保存する段階でやってしまうと、「他の出力(CSV、メール、JSON API)で逆にデコードが必要になる」 副作用が出ます。「サニタイズと出力エスケープを混同しない」 が、長く運用するときの基本姿勢になります。

代表的な攻撃と、サニタイズ / エスケープ / バリデーションの対応

攻撃カテゴリごとに 「どの処理が本命か」 を見ていきます。これが混ざるとセキュリティ実装が破綻します。

1. XSS — 出力エスケープが本命、HTML 入力サニタイズは補助

XSS(クロスサイトスクリプティング) は、「攻撃者が仕込んだスクリプトが他人のブラウザで動く」 攻撃です。

プレーンテキスト → 出力エスケープが本命

ユーザー名やコメント本文のような 「タグを許さないテキスト」 は、テンプレートエンジン(Blade、ERB、Jinja、Pug、Twig など)の 自動エスケープを切らずに使う だけで十分。「{{ \$name }}」 はそのままで安全、「{!! \$name !!}」 は危険、というイメージ

リッチテキスト → サニタイズが本命

「 太字や箇条書きは許したい」 ような場合は、出力エスケープを切る代わりに HTML サニタイザライブラリ(DOMPurify、HTML Purifier、Bleach、sanitize-html など)を通す。「許可するタグと属性のホワイトリスト」 を持つものを選ぶのが鉄則。

CSP は最終防衛線

「 Content-Security-Policy ヘッダ」 で 「インラインスクリプト不可」 や 「特定ドメインからのスクリプトのみ可」 を設定しておくと、サニタイズ / エスケープに穴があっても被害が広がりにくい。サニタイズ単体に頼らない 設計の代表例。

バリデーションも併用

「 URL 入力欄に javascript: スキームを許さない」 のような、「そもそも受け取らない」 ルールも合わせる。サニタイズ層が想定外の入力に弱いとき、入口で弾けると事故率が下がる。

XSS 対策 = サニタイズ」 と覚えると外します。テンプレートエンジンの自動エスケープが効いているかHTML を許す投稿で、危ない属性が落ちているか の二段構えが現実的です。

2. SQL インジェクション — エスケープではなくプレースホルダ

SQL インジェクション は、「入力をそのまま SQL 文に埋め込んだ結果、攻撃者が文の意味を書き換えてしまう」 攻撃です。

ここでよくある誤解が、「シングルクォートをエスケープすれば防げる」 という考え方です。これは半分正しく、半分危険 です。

読み込み中...

SQL インジェクションでやるべきは プレースホルダで値と文を分ける です。「サニタイズ」 という言葉で 「SQL 用にエスケープすればいい」 と覚えるのは、現代の Web 開発では 使わない方が安全 な発想になっています。

3. コマンドインジェクション — シェルを介さない API が本命

「 system()」 「exec()」 「Runtime.exec()」 で外部コマンドを呼ぶときに、「シェルに渡す文字列の一部をユーザー入力で組み立てる」 と、「;」 や 「&&」 や \」 で別のコマンドを差し込まれる攻撃です。

本命は 「シェルを介さない呼び出し」

Node.js の 「execFile」 / Python の 「subprocess.run([..], shell=False)」 / PHP の 「proc_open」 の引数配列など、シェルを経由せず、コマンドと引数を別々に渡す 方法を選ぶ。これだけで 「;」 や 「&&」 が 「ただの引数文字列」 として扱われる。

バリデーションで安全な集合だけ受ける

引数になり得る値を ホワイトリスト(数字だけ、特定 ID 形式だけ、特定の選択肢だけ)に絞る。「どんな文字でも来うる」 入力をコマンドラインに混ぜない。

サニタイズはあくまで補助

「 シェルメタ文字を消す」 系のサニタイズは、エンコードや別シェル(「sh」 vs 「bash」 vs 「zsh」)で挙動が変わり、確実に塞ぎ切るのが難しい。「シェルを使わない」 を第一選択にする。

そもそも外部コマンドを呼ばない

画像変換やファイル変換は、可能ならネイティブライブラリ呼び出し(Sharp、Pillow、ImageMagick の API)で完結させる。「外部コマンドの呼び出し回数自体を減らす」 のがいちばん強い対策。

4. Prompt Injection — サニタイズだけでは防ぎきれない

プロンプトインジェクション は、「ユーザー入力や取り込んだ Web ページ / ドキュメントの中に 以前の指示を無視してこうしろ のような命令が混ざっていて、LLM がそれを実行してしまう」 攻撃です。

`サニタイズで 「ignore previous instructions」 を消せば終わり」 と思いたくなりますが、現実には 無数の言い換え が存在し、「画像中の文字」 「Base64 文字列」 「多言語翻訳した命令」 などで簡単に迂回されます。

入力サニタイズは 「補助」 として残す

明らかな悪意ある決まり文句や、「システムプロンプトを表に出すリクエスト」 を機械的に弾くのは、最初の防御線として有効。ただし 「これだけで安全」 と思い込まない。

本命は 「分離と権限最小化」

LLM が触れる外部データを読み取り専用にする」 「重要操作は人間の確認を挟む」 「LLM の出力で勝手にコードを実行させない」 など、LLM が暴走しても被害が小さい設計 にする。

監視で 「事故が起きたとき素早く止める」

プロンプト全文をログに残す」 「異常な出力が出たら通知」 「API キー使用量に上限を設ける」 を組み合わせ、「完全防御ではなく被害縮小」 の方針で運用する。社内 AI 検索の prompt injection 対策 で詳しく扱った内容と同じ姿勢。

人間と同じ 「信頼できる入力源」 の発想

「 知らない人の Word ファイルをマクロ有効で開かない」 と同じく、外部から取り込んだコンテンツを 「命令」 として LLM に流さない」 が原則。「データ」 と 「指示」 を分けて扱うインターフェース設計が長期的な解。

ブラックリスト方式の落とし穴

サニタイズの実装でいちばん事故りやすいのが、危ない文字列をブラックリストで消す 方式です。

エンコーディングの差で抜ける

「 UTF-8 / Shift_JIS / Latin-1 / URL エンコード / 全角 / Unicode の同型異字」 など、見た目は同じでもバイト列が違うパターンを全部潰すのは非常に難しい。「ignore previous instructions」 を消しても 「i​gnore previous instructions」(ゼロ幅スペース挿入)で抜けたりする。

パーサの差で抜ける

「 HTML パーサ」 「SQL パーサ」 「シェル」 「LLM」 はそれぞれ独自のルールで読み取る。「自分のサニタイザで安全に見えた文字列」 が、実際の解釈エンジンでは別の意味になる、ということが頻繁に起きる。

新しい攻撃が出ると追いつかない

ブラックリストは 今知られている攻撃 しか網羅できない。最新のサプライチェーン攻撃 のような新パターンに対しては、リスト更新が後手に回る。

原則はホワイトリスト

「 許す形だけ通す」 のホワイトリスト方式は、サポートが多少面倒でも 事故率が圧倒的に低い。「HTML はこのタグだけ許す」 「引数はこの正規表現に合うものだけ」 のように、明示的に絞る発想に切り替える。

「サニタイズしてあるから安全」 という言葉が出てきたら、「 ブラックリストかホワイトリストか」 を必ず確認する 癖をつけると、コードレビューでの事故検知率がぐっと上がります。

実務での使い分けチェックリスト

「 バリデーション / サニタイズ / エスケープ」 が混ざらないように、実務では次の順で考えると整理しやすくなります。

読み込み中...

このフローを 「処理の場所と目的の対応表」 として持っておくと、レビューや設計で 「この場所にこの処理を入れていいか?」 の判断が速くなります。

サニタイズに関するよくある質問

Q. PHP の 「htmlspecialchars」 はサニタイズですか?

A. 厳密にはエスケープ です。HTML の文法に合わせて 「<」 や 「>」 を実体参照に変換するもので、「元の値を捨てる」 タイプの加工ではありません。「HTML 出力時のエスケープ」 が本来の役割で、「サニタイズ」 と呼ぶと用途を取り違える原因になります。

Q. サニタイズは入力時にやるべきですか? 出力時にやるべきですか?

A. 用途と場所による が答えです。HTML 入力フォームのリッチテキストなら 「保存前にサニタイズ」、テンプレ出力なら 「出力時にエスケープ」、ログ出力なら 「書き出す直前にマスキング」 と、「そこでの危険性を、そこで取り除く」 のが原則です。一律 「入力時にやる」 と固定すると、別の出力先で逆にデコードが必要になって混乱します。

Q. バリデーションを書けば、サニタイズは不要ですか?

A. テキスト系の単純な入力は不要 ですが、「HTML や Markdown のような構造を持つ入力」 を扱うなら、「バリデーションで弾き切れない」 ので両方必要です。「バリデーション = 受けるかどうか」、「サニタイズ = 受けた中身の危険要素を取り除く」 と覚えてください。

Q. プレースホルダを使えば SQL インジェクション は完全に防げますか?

A. 値の部分は安全になります。ただし 「ORDER BY のカラム名」 や 「テーブル名」 のような 値ではない部分 は、プレースホルダで扱えないので、必ずホワイトリストでチェックします。「動的にカラム名を変える」 ような実装には別の注意が必要です。

Q. フレームワークの自動エスケープがあれば XSS は心配ないですか?

A. ほぼ大丈夫だが油断は禁物 です。「{!! $value !!}」(Blade)や 「dangerouslySetInnerHTML」(React)のように 自分で自動エスケープを切るシンタックス を使ったときに穴が空きます。「HTML 属性に値を埋め込むときの引用符の扱い」 や 「JavaScript 文字列リテラル内の埋め込み」 のように、HTML と JS で文脈が違うケースも要注意です。

Q. AI に渡すプロンプトの 「サニタイズ」 は何を消せばいいですか?

A. 機密情報 / 個人情報 / 社外秘の固有名詞マスキングするのが基本です。一方、プロンプトインジェクション 対策としての 「攻撃文の除去」 は完全には無理なので、「サニタイズしたから安全」 とは思わず、権限最小化・人間の確認・監視 を併用してください。

Q. ブラックリスト方式のサニタイズはダメですか?

A. 補助としては有効、本命としては危険 が現実的なところです。「明らかな攻撃文字列を弾く」 程度なら一次防御として役に立ちますが、それだけで安全と判断するのは事故のもとです。「 許す形だけ通す」 ホワイトリスト を基本に据え、ブラックリストは追加防御で使う、という整理が安全です。

まとめ

サニタイズは 「入力や出力から危険な要素を取り除く処理」 を指す広い言葉で、「 どこで、何を、どう削るか」 をはっきりさせないと意味が薄れます

エスケープ・バリデーションと混同せず、攻撃の種類ごとに どの処理が本命か を意識して設計するのが、長く運用しても破綻しないコードの条件です。「サニタイズしてあるから安全」 という言葉を聞いたら、「どこで、何を、どう?」 の3つを確認するクセを付けておくと、レビューでも開発でも事故の芽を早めに摘めます。

参考リンク

あとで見返すならここで保存

読み終わったあとに残しておきたい記事は、お気に入りからまとめて辿れます。