この記事はユニークビジョン株式会社 Advent Calendar 2020の記事です。
去年は、「1日目から長い」と言われたので少し前にやった勉強会の内容を改めて記事にすることでここはひとつ。
内容は表題の通り。
Twitter API で取得したツイートはエスケープがかかったままであるという内容。
ツイートの取得
まずは API でツイートを取得
回収するツイートはこれ
#20201124_test_tweet black&white とツイートした内容
require 'oauth'
require 'json'
consumer = OAuth::Consumer.new(
"YOUR CONSUMER KEY",
"YOUR CONSUMER SECRET",
site:'https://api.twitter.com/'
)
endpoint = OAuth::AccessToken.new(
consumer,
"YOUR ACCESS TOKEN",
"YOUR ACCESS SECRET"
)
id = '1331104166027018241'
show_url = 'https://api.twitter.com/1.1/statuses/show.json'
res = endpoint.get(show_url+"?id=#{id}")
puts JSON.parse(res.body)['text']
# => #20201124_test_tweet black&white
& がエスケープされて返ってきました。
ネット上の情報
2007年ごろから同様の現象が報告されているのを発見した。
Twitter API の提供開始が2006年9月ごろからとのことなので、この現象はAPI提供開始から継続していると考えられる。
なお、Twitter API v2 でも依然確認できる事象なので今後もこのままだろう。
ただし、古い記事と比較的新しい記事では報告内容が微妙に異なる。
いわく、古い記事では「<」, 「>」 がエスケープされている。
いわく、新しい記事では「<」, 「>」, 「&」 がエスケープされている。
では、当記事投稿時点での実際の挙動を確認してみる。
現在の挙動
ascii 記号を全て含めたツイートを行い、これを取得してみる。
ツイートはこれ
# (略)
id = '1331107019990790145'
show_url = 'https://api.twitter.com/1.1/statuses/show.json'
res = endpoint.get(show_url+"?id=#{id}")
puts JSON.parse(res.body)['text']
# => #20201124_test_tweet !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
新しめの記事と同じく、「<」, 「>」, 「&」 がエスケープされていることを確認できた。
また、古い記事では
「エスケープされた結果がDBに直接保存されていて、それを画面に表示しているらしい。」
「実体参照をツイートするとデコードされる。<
とツイートするとTwitter上で < に文字化けする。」
と記述されているが、この現象は確認できなかった。
古い記事で指摘された妙な XSS 対策は幾ばくか改善しているらしい。
対象文字
さて、対象文字はなんなのか。
というかどういったエスケープをかけているのか。
私も & が &
に化けて返ってきた時はHTMLエスケープをしているのかと思ったのだが、" などがエスケープされていないのでどうも違うらしい。
そこで以下のテストをした。
require "cgi"
str = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
puts CGI.escapeHTML(str)
# => !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
puts str.encode(xml: :attr)
# => "!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
puts str.encode(xml: :text)
# => !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
XML の CharData として処理してると考えると API の結果と一致する。
まとめ
Twitter API はテキストを XML の CharData としてエスケープ処理をかけて返却することがわかった。
ただし、このあたりの仕様が少なくとも一度変化している様に見えるため、Twitter が未来永劫エスケープしたテキストを返してくるとは信用しきれない。
自身のサービスに取得したツイートの内容を表示する場合、Twitter 側でのエスケープは信用せず一度デコードしてから再度任意のエスケープをかけるなどするのが迂遠であるが妥当な対応なのかもしれない。
参照記事
徳丸浩の日記 2007年08月29日 TwitterのXSS対策は変だ
こせきの技術日記 2010-04-14 Twitterのエスケープ処理について。
岩本隆史の日記帳(アーカイブ) 2010-04-15 Twitterの中途半端なエスケープにどう対応するか
約束の地 2016-06-17 Twitter の API で取得できるデータの一部はエスケープ済みである