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

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

サニタイズは 「入力や出力から危険な要素を取り除いて無害化する処理」 ですが、エスケープやバリデーションと混同されがちです。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つを確認するクセを付けておくと、レビューでも開発でも事故の芽を早めに摘めます。

参考リンク

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

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