読者です 読者をやめる 読者になる 読者になる

S.F. Page

Programming,Music,etc...

script要素中のHTMLデータのエスケープと、JSON-LDデータのエスケープを考える。

自前ブログシステムのデータの持たせ方を考えている。 アイデアとしては以下のような、HTML中にJSON-LDデータを埋め込む形を考えている。

JSON-LDにコンテンツデータを持ち、それをスクリプトでレンダリングして表示するのである。で、コンテンツデータは当然HTMLタグ混じりとなる。 だがJSON-LD中に埋め込んだHTMLのせいで、このページはエラーとなってきちんと表示されない。

エスケープ処理が必要なのだ。

JSONのエスケープ

JSON-LDはJSONであるので、RFC7159によれば、文字列はエスケープ処理をする必要がある。

7.Stringsを引用・翻訳すると、

文字列表現はCファミリーのプログラム言語の規約と同様である。文字列はクォーテーション・マーク(")で始まり、終わる。 エスケープしなければならない文字(逆斜線(バック・スラッシュ \)、クオーテーション・マーク(")、制御文字(U+0000からU+001F))を除いて、Unicodeキャラクターはクォーテーション・マーク内に配置できる。

任意の文字をエスケープすることができる。文字が基本多言語面(U+0000からU+FFFF)中にある場合、逆斜線、続いて小文字のu、続いて文字コードをエンコードした4桁の16進数の6文字のシーケンスで表現することができる。A-Fまでの16進文字は小文字もしくは大文字のいずれかにできる。例えば、一つの斜線文字(\)のみを含む文字列は"\u005C"として表される。

代わりに、いくつかのよく使われる文字のための2文字のシーケンスによるエスケープ表現がある。例えば、一つの斜線文字(\)のみを含む文字列は"\\"として表される。

基本多言語面内ではない拡張文字を使用するためには、文字はUTF-16のサロゲート・ペアとしてエンコードし、12文字のシーケンスとして表す。例えばGクリフ文字(U+1D11E)のみを含む文字列は"\uD834\uDD1E"として表わされる。

文字列のABNF:

    string = quotation-mark *char quotation-mark

      char = unescaped /
          escape (
              %x22 /          ; "    quotation mark  U+0022
              %x5C /          ; \    reverse solidus U+005C
              %x2F /          ; /    solidus         U+002F
              %x62 /          ; b    backspace       U+0008
              %x66 /          ; f    form feed       U+000C
              %x6E /          ; n    line feed       U+000A
              %x72 /          ; r    carriage return U+000D
              %x74 /          ; t    tab             U+0009
              %x75 4HEXDIG )  ; uXXXX                U+XXXX

      escape = %x5C              ; \

      quotation-mark = %x22      ; "

      unescaped = %x20-21 / %x23-5B / %x5D-10FFFF

そういうことで、JSON文字列は上記の規約に従ってエスケープしなければならない。JavaScriptであればJSON.stringify()メソッドによって適切にエスケープしてくれるようである。 必ずエスケープしなければならない文字は逆斜線(バック・スラッシュ \)、クオーテーション・マーク(")、制御文字(U+0000からU+001F)である。

HTMLのSCRIPT要素埋め込み内のHTMLのエスケープ

HTML内のSCRIPT要素内にJSONが埋め込まれると、JSON内の文字列にHTML要素が含まれる場合、その文字列がブラウザによってHTMLとして解釈されないようにエスケープ処理する必要がある。

JSONのエスケープ | yohgaki's blogによれば、

HTMLに埋め込む場合にはHTMLパーサーに誤ってHTML要素の一部として解釈されないように、HTMLの特殊文字(< > " ' & / )をエスケープしなければなりません。

である。

JSONは任意の文字をエスケープすることができるので、

  • <\u003c
  • >\u003e
  • "\"
  • &\u0026
  • `\u0027
  • /\u002f

でエスケープすることで、回避することができるだろう。

script要素のコンテンツの制約

HTML5の仕様を読んでいたところ、下記のことが書いてあった。

4.11.1.2 script要素のコンテンツの制約
この節で説明される多少奇妙な制約を回避する最も簡単で安全な方法は、以下のシーケンスが(たとえば文字列、正規表現、またはコメントなど)スクリプト内でリテラルに表示され、かつ式の中でこのような構造体を使用してコードを記述を避けるようにする際に、"<!--"を"<!--"として、"<script"を"<\script"として、および"</script"を"<\/script"として常にエスケープすることである。この節の制限がトリガーになりやすい落とし穴を回避する:すなわち、歴史的な理由のために、HTMLにおけるscriptブロックの解析は、これらのシーケンスを考えた場合に直観的でない働きをする奇妙で風変わりな慣習である。

これはHTMLのSCRIPT要素埋め込み内のHTMLのエスケープを施すことにより回避できそうだな。