HTML – コメントされたCDATAセクションもどきはやめよう

突然だが,Webフロントエンドにかかわったことがある人ならば以下のようなHTMLソースを見たことがあるはずだ.

<script>
//<![CDATA[
...
//]]>
</script>

あるいは

<style>
/*<![CDATA[*/
...
/*]]>*/
</style>

今回は,このような記法がなぜ無意味であるかを説明しよう.

そもそも,このような書き方にはどのような意図があるのだろうか.

「エスケープされたCDATAセクションもどき」の由来

伝統的には,HTMLの”地”の部分には<, >っといったタグに使われる記号はかけないことになっている.ではどうするか.

HTML/XMLには &lt; や &gt; という記法を使う実体参照 (entity references) という仕組みが用意されている.

&lt; = <
&gt; = >
&amp; = &
&quot; = "
&apos; = '

素直に考えれば,<script>や<style>のタグの中身をこの方法を使ってエスケープして書けばいいはずだ.

ところが,HTML文書(99%以上のWebページがこれによって書かれていると思われる)では,互換性の問題が発生する.

歴史的事情により,Content-Type: text/htmlで送信されるHTML文書において以下のようなコードを書いたとしても,実体参照 (&lt;) が認識されずエラーになる.

<script>
for (let i = 0; i &lt; 10; ++i) {
console.log (i);
}
</script>

そこで考え出されたのが<![CDATA[の使用だ.

CDATAセクションとは,XMLにある仕様の一部で,その中に書かれたタグを (”]]>”以外)無視させるというものだ.

これを素直に使おうとすれば以下のようになる.

<script><![CDATA[
for (let i = 0; i < 10; ++i) {
console.log (i);
}
]]></script>

ところが世の中のほとんどのWebページはHTMLモードで書かれているので,CDATAセクションは実際には認識されない.つまりスクリプトやスタイルシートの一部と認識されてエラーになる.

これを回避しようと生み出された奇妙な記法が「コメントされたCDATAセクションもどき」だ.

JavaScriptとCSSは外部ファイル化しよう

一番簡単な迂回方法は,<script>と<style>をインラインで書くのをやめることだ.実際,近年XSS (Cross-Site Scripting) などのセキュリティ脅威に対する対策が求められる中で最も有効な防御策とされている Content Security Policy ではデフォルトでインジェクションに対して脆弱なインラインのスクリプトやスタイルシートを書けないようになっている.

<link rel='stylesheet' integrity='sha384-...' href='/path/to/stylesheet.css'/>
<script async integrity='sha384-...' src='/path/to/script.js'></script>

根本策はXHTML

なお,これらの問題は,XHTMLを使うことできれいに解決する.

重要なのでここでおさらいしておこう.”XHTML”とは何か.

XHTMLとは,簡単に言えばXMLのアプリケーションとして書かれたHTMLのことである.XMLはXMLのMIMEタイプ(Content-Type HTTPヘッダで送信されるもの)を持つ.XHTMLの場合はapplication/xhtml+xmlである.Webでは,XHTMLのつもりで書かれた文書がtext/htmlで送信されることが常態化している.その場合,ユーザーエージェントはXHTMLではなくただのHTMLとして解釈してしまい,XMLの機能は使えなくなってしまう.

XHTMLは正規のXMLである.つまり前述の実体参照もきちんと認識されるし,本当のCDATAセクションも使える.

なお,わたしは新規に開発されるWebアプリケーションにはapplication/xhtml+xmlのMIMEタイプを持つ正規のXHTMLモードを採用することを推奨する.XMLは,伝統的にHTMLが持つさまざまな複雑性から脱出するために策定された仕様でもあるからだ.

このために必要なことは主に以下の2つだ.

  • ドキュメントを文法エラーが一切ないwell-formedにすること
  • Content-Type: application/xhtml+xmlで文書を送信すること(重要)←これをしないとHTML文書として解釈されてしまう