Yueseryueser.com

← ガイド一覧

JSON・HTML・SQL のエスケープ:3つの文脈を混同しない

対象の文脈(JSON 文字列、HTML エンティティ、SQL リテラル)を選び、テキストを貼り付けて「エスケープ」を押します。逆方向は「元に戻す」です。JSON モードが返すのは文字列の内側の内容で、前後のダブルクォートは含まれません。

ツールを開く:Text Escape

エスケープが本当に必要な場面

JSON のリクエストボディを手で書いていて、ユーザー入力を1つの文字列値に入れたいとき、JSON モードは引用符の内側に入れるべき内容をそのまま返します。バックスラッシュ、ダブルクォート、改行、タブはすでに処理済みです。

多層エスケープの確認では、" のような断片を HTML モードで1回戻してみます。まだ " が残っていれば2回以上エスケープされているので、読める形になるまで戻します。

データを目視確認するために SQL リテラルを手で組み立てるのもよくある場面です。文法エラーで止まらないようにするためのもので、アプリケーションのコードに使うものではありません。

失敗しないための手順

JSON モードは1つの文字列値だけを扱い、文書全体の妥当性は検証しません。括弧の対応もカンマも見ないため、全体を検証したいときは JSON 整形ツールを使ってください。

元に戻すときは入力全体を1つの JSON 文字列として解析します。そのためエスケープされていない引用符があると、途中までの結果ではなくエラーになります。欠けたエスケープ列の意味を推測する用途には使えません。

結果を HTML の <script> の中に入れる場合、JSON のエスケープだけでは足りません。加えて </script> と U+2028、U+2029 も処理しないと、スクリプトが途中で閉じたり文法エラーになったりします。

HTML モードが置換するのは &、<、>、ダブルクォート、シングルクォートの5文字だけです。テキストがタグとして解釈されるのを防げますが、XSS 対策にはなりません。on* イベント属性、javascript: の URL、引用符のない属性はそのまま通ります。ユーザー投稿の HTML を無害化するには DOMPurify のようなホワイトリスト方式のサニタイザーが必要です。

SQL モードはシングルクォートを2つに増やすだけで、標準 SQL でリテラルを引用する書き方です。MySQL は既定でバックスラッシュをエスケープ文字として扱うため、バックスラッシュを伴う引用は破れます。数値の位置、ORDER BY の列名、LIKE のワイルドカードは守備範囲外です。

エスケープは冪等ではありません。1回目で & が &amp; になり、2回目で &amp; の中の & がさらに &amp;amp; になります。画面に &amp; や &amp;amp; がそのまま表示されるときは、想定より1層多くエスケープされている可能性があります。対象の文脈へ書き込む瞬間に1回だけ行ってください。

代替手段と限界

JSON は言語標準のシリアライザーに任せてください。JSON.stringify はエスケープを正しく処理し、オブジェクト全体を一度に扱えます。

HTML はテンプレートエンジンの自動エスケープか、標準ライブラリの html.escape を使ってください。リッチテキストの無害化は、自作の正規表現ではなく DOMPurify のようなホワイトリスト方式のライブラリで行います。

SQL はプレースホルダー(? や $1)を使ったパラメーター化クエリやプリペアドステートメントを使ってください。値はデータとして渡し、文字列に連結しません。数値、識別子、ワイルドカードまで守れるのはこの方法だけです。

このツールの限界は明確です。JSON モードは1つの文字列値のみ、HTML モードは5文字のみ、SQL モードはシングルクォートを2倍にするだけで、MySQL のバックスラッシュモードや数値の位置は守れません。

よくある質問

SQL のエスケープだけで SQL インジェクションは防げますか?

それだけでは防げません。SQL モードはシングルクォートを2つに増やすだけで、これは標準 SQL でリテラルを引用する書き方です。MySQL は既定でバックスラッシュをエスケープ文字として扱うため、バックスラッシュを伴う引用は破れます。GBK のようなワイド文字エンコーディングには歴史的に独自の回避手法があり、数値パラメーター、ORDER BY の列名、LIKE の % や _ は引用符では守れません。アプリケーションのコードでは、プレースホルダーを使ったパラメーター化クエリやプリペアドステートメントを使ってください。エスケープは補助的な手段にとどめます。

JSON のエスケープ結果に前後のダブルクォートがないのはなぜですか?

文字列の内側の内容を返しているためです。既にある引用符の間にそのまま入れられるようになっています。完全な JSON リテラルが必要なときは、外側の引用符を自分で補ってください。元に戻すときは入力全体を1つの JSON 文字列として解析するため、エスケープされていない引用符があると、途中までの結果ではなくエラーになります。

2回エスケープすると & が &amp;amp; になるのは不具合ですか?

不具合ではありません。エスケープは冪等ではなく、1回目で & が &amp; になり、2回目で &amp; の中の & がさらにエスケープされます。画面に &amp; や &amp;amp; がそのまま表示されるのは、想定より1層多くエスケープされているためです。対象の文脈へ書き込む瞬間に1回だけエスケープしてください。すでにエスケープされたデータを受け取った場合は、何層になっているかを確認してから、まとめて元に戻します。

関連ツール:Text Escape