岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

Twitterの中途半端なエスケープにどう対応するか

TwitterのつぶやきをAPIで取得すると、中途半端にエスケープされた文字列が返ってくる

id:koseki2さんが「Twitterのエスケープ処理について。 - こせきの技術日記」で指摘されているように、TwitterのつぶやきをAPIで取得すると、中途半端にエスケープされた文字列が返ってきます。

つぶやき <"&hearts;&&hearts;">
XML APIレスポンスの該当部分 &amp;lt;"&amp;hearts;&amp;&amp;hearts;"&amp;gt;
JSON APIレスポンスの該当部分 "text":"&lt;\"&hearts;&&hearts;\"&gt;"
該当部分をアンエスケープすると &lt;"&hearts;&&hearts;"&gt;

アンエスケープ後の文字列を元のつぶやきと比較すると、「<」と「>」のみエスケープされていて、「&」と「"」はされていないことが分かります。

APIの利用者にしてみれば、エスケープされていない、ユーザがつぶやいたままの文字列を返してほしいところです。というか、普通はそうするんじゃないかと思います。

しかしTwitterは「TwitterのXSS対策は変だ: Twitterのクロスサイト・スクリプティング(XSS)対策は変だ - 徳丸浩の日記(2007-08-29)」で徳丸さんが推測されているように、中途半端エスケープ後の文字列をDBに保存しているため、もはや復元不能で、元の文字列を返せないのでしょう。

エスケープなしでの出力は怖くてできない

以上の前提をふまえて、つぶやきを取得・表示するWebサービスを作るとしたら、この中途半端なエスケープにどう対応したものでしょうか。

id:koseki2さんは「Twitterを信じて、与えられたテキストをそのままHTMLで出力していいものか」と書かれています。そのまま出力する場合、Twitter上での表示とWebサービス上での表示が一致するというメリットがあります。

つぶやき <"&hearts;&&hearts;">
Twitter上での表示 <"♥&♥">
Webサービス上での表示:そのまま出力 <"♥&♥">

しかし、僕*1なら、エスケープなしでの出力は怖くてできません。今後Twitterの仕様が変わり、ユーザ入力値がそのまま送られてくる可能性がないとはいえないからです。脆弱性にもなりますし、HTMLが壊れることもありえます。

「&lt;」「&gt;」のみアンエスケープするのが僕の好み

エスケープすると決まれば、あとは中途半端にエスケープされた文字列をどこまでアンエスケープするのか、それともしないのかを考えればよいだけです。以下の3方式が考えられます。

  1. 「&lt;」「&lt;」「&amp;」「&quot;」をアンエスケープ
  2. 「&lt;」「&gt;」のみアンエスケープ
  3. いっさいアンエスケープしない

僕の好みは2です。さきほど書いたことと矛盾するようですが、Twitterの中途半端エスケープ仕様は今後も変わらないと推測されます。データ量が莫大だからです。

また、その推測にたてば、1は冗長な処理になりますし、3は二重エスケープが問題になります。

つぶやき <"&hearts;&&hearts;">
Twitter上での表示 <"♥&♥">
Webサービス上での表示:「&lt;」「&lt;」「&amp;」「&quot;」をアンエスケープ <"&hearts;&&hearts;">
Webサービス上での表示:「&lt;」「&gt;」のみアンエスケープ <"&hearts;&&hearts;">
Webサービス上での表示:いっさいアンエスケープしない &lt;"&hearts;&&hearts;"&gt;

実体参照を許す必要はあるのか

話がそれますが、そもそも、なぜ中途半端なエスケープ処理をTwitterは採用しているのでしょうか。おそらく、つぶやき中の実体参照を許したいからなのでしょう。が、許す必要があるのかどうか僕には疑問です。「<"♥&♥">」と表示させたければ、ユーザが「<"♥&♥">」とつぶやけばよいだけではないでしょうか。

*1:今後「僕」を一人称に使います。どうでもいいですか