NIKKEI TECHNOLOGY AND CAREER

いにしえのXSSと、現代における発展

こんにちは。ソフトウェアエンジニアの淵脇です。この記事は NIKKEI Advent Calendar 2020 5日目の記事です。私は「日経ID」といわれる認証認可基盤を担当しており、日々セキュリティを意識した開発を行っております。それに関連して、本日はいにしえのブラウザにおけるセキュリティの歴史的なものをご紹介いたします。

0. はじめに

現代ではブラウザはとても便利なものになりました。単にネットサーフィンのようなオンラインで完結する作業だけでなく、買い物やコミュニケーション、金融取引など実世界に影響を与えるような様々な用途に使われています。しかし、この裏には血のにじむようなセキュリティエンジニアとハッカー/クラッカーとの戦いの歴史があり、その上に築かれたセキュリティの上に成り立っているものです。

ブラウザのセキュリティについては、昔からXSS(クロスサイトスクリプティング)やCSRF(クロスサイトリクエストフォージェリ)といった、フロントエンドのHTMLやJavaScriptに起因するものが大きな割合を占めてきました。こういったフロントエンドに起因する脆弱性はわかりやすいものが多いため、以前は主に「いたずら」に使われることが多かったようです。XSS/CSRFに興味のあるちょっと年齢層の高い人であれば、mixiで話題になったぼくはまちちゃんなどは覚えている方もいらっしゃるのではないでしょうか。

筆者はだいぶインターネット老人会[1]に足をつっこんでいることもあり、1990年代後半〜2000年代前半における日本で流行った「いたずら」として有名であったいわゆる「XSS[2]」を長いこと見てきました。本日はそのような過去の歴史をご紹介したいと思います。また、一部については現代にその発想が受け継がれているものもあったりします。それらについても軽く触れたいと思います。

基本的にこれらの内容は

Sorry! Internet Explorer Only!

です。さらに IE といっても Version 3〜6 といった相当古い時代のものでしか通用しない話になります。そのような古い実機環境が手元になかったため、記憶に頼っている箇所があり不正確な記述がある可能性があることをご容赦ください。

目次

1. 投稿したらGETだった件

まずは次のコードをご覧ください。


<a href="http://danger.xss-ready.com/caution/bbs.cgi?name=hoge&title=xxxxx&value=aaaaaaa">

何の変哲もないリンクであり、ついついクリックしてしまったあなたですが・・・

掲示板

リンク先を見ると何者かに荒らされていました。ひどいことをする人がいるものですね。 一体どうしたのでしょうか?

1-1. HTTPメソッド

HTTPとは簡単に言えばブラウザがWebサーバにアクセスして、Webサイトのデータを取得するための約束事(プロトコル)です。このプロトコルはWebの様々な要請に答えるため膨大な仕様になっているのですが、その中でまずWebサーバに大まかに何がしたいかを伝えるために「HTTPメソッド」という情報を伝えます。「HTTPメソッド」にはいろいろな種類がありますが、主には次のようなものがあります。

  • GET データの取得に使う。アクセスのほとんどはこれ。
  • POST データの投稿に使う。GET以外のほとんどはこれ。
  • HEAD ヘッダーだけ取得した場合に使う。生存確認にちょっと使われている。
  • DELETE RESTful API等でデータの削除に使う。使用頻度は少なめ。
  • PUT RESTful API等でデータの更新に使う。使用頻度は少なめ。
  • その他色々あるが限定的な場面で使う感じ。

このうち、ブラウザでアクセスする場合は「GET」及び「POST」が主に用いられます。一部の仕様で「OPTIONS」や「HEAD」を使うケースもありますがそんなに数は多くないです。

1-2. GET による投稿

GET と POST は通常のブラウザでのアクセスで用いられる2大メソッドですが、これらの使い分けは非常に重要です。なぜなら GET はサーバ側のリソースに影響を与えないことを前提にブラウザの挙動が組まれているからです。しかし当時の掲示板では、ユーザの入力に対して次のようなコードが書かれていることが結構ありました。


if ($ENV{REQUEST_METHOD} eq 'POST') {
  read(STDIN,$buffer,$ENV{'CONTENT_LENGTH'});
} else {
  $buffer = $ENV{'QUERY_STRING'};
}
foreach $pair (split(/&/,$buffer)) {
  ($name,$value) = split(/=/,$pair);
  $FORM{$name} = decode($value);
}
...
# 以下、%FORM を使って掲示板の投稿処理

HTTP のリクエストでは POST におけるボディのエンコードと、 GET のクエリにおけるエンコードが似たようなものであったため、このような手法をとっていたCGIが結構あったのです。そのため、意識せずにGETのクエリにフォームで指定されるべきパラメータを指定することで投稿できてしまうというセキュリティホールがよくありました。最初に出てきたリンクはこのパラメータを付与したGETリクエストであったため、リンクが踏まれるたびに掲示板に新たに投稿がなされてしまう、というものだったのです。つまり、掲示板に意味不明な投稿をしていたのはリンクを踏んだあなただったのです。 ちなみに現在では HTTP リクエストの処理をこのようにスクラッチで記述することは少なくなっており、POST と GET をちゃんと使い分けているサービスがほとんどですのでご安心ください。

2. 悪役イメタグなのでJavaScriptを飼ってみました

まずは次のコードをご覧ください。


<img src="javascript: for(;;) {alert('Hello world!')}">

あなたはふと訪れた掲示板に壊れた画像が投稿されている (今では信じられませんが、当時はHTMLの一部をそのまま投稿できる掲示板が結構あったのです) のを目撃しました。

掲示板

その直後、ブラウザにダイアログが現れました。

Hello world!

しかしそのダイアログ、消しても消してもまた復活してきます。あなたは仕方なく Ctrl + Alt + Delete でブラウザを落としました・・・

2-1. ブックマークレット

今ではブックマークレットと言われるものですが、URLの代わりに「javascript: 〜〜」という形でスクリプトを記述するとブラウはそれをJavaScriptと解釈して実行するという機能があります。昔のブラウザではこれが例えばimgタグのsrc属性であったとしても実行するという仕様が長らく残っていました。そのため、imgタグを本文にかける掲示板や、サニタイズが不十分な画像のURLを投稿できる掲示板などではその中にスクリプトを記載されてしまうというセキュリティホールがありました。今回のコードをその仕様を使ったものでした。ちなみに現在は img タグでのこのような JavaScript 実行はできなくなっていますのでご安心ください。

2−2. 亜種

imgタグだとわかりやすいのですが、次のような亜種もありました。


<object data="javascript: alert(1)">
<embed src="javascript: alert(1)">

サニタイジングをタグ名のホワイトリスト形式で行っていたところなどは、このようにブラウザの解釈するHTMLの拡張についていけず、imgタグは禁止したがこれらのタグはスルーしてしまうなどの穴がありました。そういう場合などは積極的に狙われイタチごっこのような形でサニタイジングの強化が進み、今ではそもそも投稿する本文にタグを許可しないというのは常識になっています。

2-3. GET での投稿との組み合わせ

また、スクリプトに限らず前節のようにGETでの投稿と組み合わせると次のようなこともできてしまいます。


<img src="http://danger.xss-ready.com/caution/bbs.cgi?name=hoge&title=xxxxx&value=aaaaaaa">

このようなimgタグが存在するページを開くと、そのユーザはクリックなどのアクションなく掲示板に投稿させられてしまう、ということもできてしまいました。

これだけだと単なるいたずらですが実はプライバシー上かなり危険な点も指摘されています。例えばSNSなどで、誰かのページを訪問すると訪問した人のIDが残るいわゆる「あしあと機能」というものがあったのを覚えている方も多いかと思います。そういったSNSのアカウントを十分な個数用意し、「http://sns.jp/mypage-<番号>」というマイページを30個用意したとします。このとき、例えば2進数で「111010」という会員番号の人に対して、ビットの立っている番号だけSNSにアクセスさせるような、そんなimgタグがあったとしましょう。例えば次のような形になります。


<img src="http://some-sns.jp/mypage-0">
<img src="http://some-sns.jp/mypage-1">
<img src="http://some-sns.jp/mypage-2">
<!-- <img src="http://some-sns.jp/mypage-3"> -->
<img src="http://some-sns.jp/mypage-4">
<img src="http://some-sns.jp/mypage-5">
<!-- <img src="http://some-sns.jp/mypage-6"> -->

すると、自分のサイトにアクセスしてきた人の会員番号と、SNSの紐付けをすることが可能になってしまうのです。SNSには個人情報が書かれていることも多いため、かなり重大なプライバシー漏洩のリスクとなってしまいます。

3. 異世界はスタイルシートとともに

まずは次のコードをご覧ください。


<font style="color:#FFFFFF; background: url(javascript:for(;;){alert(Hello world!)})">

近年HTMLの仕様拡張が進み、リッチなデザインが実現できるCSSというものが流行っているようです。いつも訪れている掲示板にも本文の色を指定できる機能がついたようなので早速見に行ってみました。

掲示板

一見、先程のような壊れたimgタグなどもなく、すべての投稿が健全な掲示板に見えます。しかし、また同じく例の無限ダイアログが出てしまいました。

Hello world!

あなたは仕方なく Ctrl + Alt + Delete でブラウザを落としました・・・

3-1. スタイルシートなら安全?

デザインをユーザー指定させる場合、タグをそのまま挿入できるような仕様は直感的に危険性がわかりやすいのですが、スタイルシートにすることで安全性を確保できるという説が一部であったこともあり、タグの挿入を禁止する代わりにこのようにスタイルシートを色々触れるようにしていたケースもあったようです。

しかし、冒頭でお見せしたように色の指定をスタイルシートでさせただけでもセキュリティホールができてしまいました。これは主に次のようなコードで発生したようです。


<font style="color:<ユーザーが指定した色>">

ダブルクォーテーション等をサニタイジングすれば一見安全に見えるのですが、前節で説明したブックマークレットの原理と組み合わせることで、最初に提示したようなスクリプトを挿入されてしまうという危険性があり、結局またしてもやられてしまいました。

3-2. サニタイジングの難しさ

ここまで聞くと、単に「<」「>」等の特定の文字を気をつければ済むことでは?という気もしてきます。しかし例えば「UTF-7を使うことで直接記述せずに『<』『>』等の文字を表す」といったパッと見では気づかないような怖いものもあったりします。こういったテクニックは XSS Challenge という名前で CTF やセキュリティ界隈で勉強することができるので、興味のある方はぜひ覗いてみてください。

4. とあるスクリプトの自己言及(クワイン)

さて、ちょっと話は変わりますが皆さんは自己言及コード(クワイン)を書いたことはありますでしょうか? 例えばJavaScript(Node.js)でコンソールログに出力するパターンでは下記のように書くことができます。


eval(x="y=String.fromCharCode(34);console.log(('eval(x='+y+x+y+')'))")

実はクワインはこのようにevalやソースコード自体を読み込んで出力するといったお手軽な方法の他にも、そのような抜け道を使わない純粋なロジックだけで実現する方法もあります。さて、ここでちょっと発想を変えてHTML上のアンカータグとして書いてみましょう。


<a id="link1" href="javascript: d=String.fromCharCode(34); alert('<a id=' + d + 'link1' + d +  ' href=' + d + document.getElementById('link1') + d + '>Link</a>')">Link</a>

するとこのように HTML のクワインがアラートとして表示されたのがわかるかと思います。

クワイン by HTML

これは単にアラートダイアログとして出力した例ですが、例えば掲示板などで「自己言及コードにより自分自身を投稿させるリンク」というものが貼られたら、そのリンクは延々と自己増殖していくことになります。まるでワームみたいで怖いですね。

5. 限りなく透明に近いiframe

まずは次のコードをご覧ください。


<iframe src="http://sns.jp/mypage12345" style="opacity: 0.2; position:absolute;"></iframe>

いつもの掲示板に「ここをクリック!」というリンクがあったのでついついクリックしてしまいました。

掲示板

なんかうっすらと変なものが浮かんでいて不安ですね。案の定怖いメッセージが出てしまいました・・・

クリックジャック

5−1. HTML frame の登場

まだネットスケープが元気だった時代、ブラウザに「frameタグ」がサポートされました。これは今まで1ページ 1 HTMLだった時代においてよりリッチなWebサイトを実現する画期的なタグで、またたく間にフレームを多用したページは増えていきました。


<html>
<head>
  <title>私のホームページ</title>
</head>
<frameset cols="50%,*">
  <frame src="frame1.html" name="frame1">
  <frame src="frame2.html" name="frame2">
  <noframes>
    このページはフレーム対応のブラウザでご覧ください。
  </noframes>
</frameset>
</html>

おそらくフレームの当初の目的は、右にメニューを表示する、position absolute で下に常にフッターを表示するなどといった使い方が想定されていたものと思います。しかし、フレームは本来支配下にあるべきでない他のサイトのページを表示でき、またJavaScriptを用いれば操作もできる、ということに気づいた人もいて XSS の温床になりました。そのため、「JavaScriptで自サイト以外のframeのドキュメントを操作する」については後々禁止されました。

5−2. iframe の登場

フレームを用いると画面がタイル分割される独特なデザインになるため、大抵は「フレームが使われている」ということが認識しやすいページになります。しかし、ここで新たに登場した要素がインラインフレーム(iframe)です。iframe はドキュメント内のオブジェクトとしてのフレームを実現することが可能であり、サイトを見てもフレームが使われていることがとてもわかりにくいものになりました。実は HTML5 においては本家の frame は廃止され iframe のみが残りました。たまに目にする Twitter の埋め込みなどは iframe を用いて実現されています。

掲示板

また一部の広告についても iframe が使われるなど現代のリッチなサイト構成にとっては欠かせないタグになったため、本家は HTML5 で廃止される一方で iframe は生き残ったようです。

5-3. OAuth認証認可

TwitterなどでおなじみのOAuth認可はボタンひとつで外部アプリにBotなどの仕組みを実現する画期的なプロトコルです。OAuthの認可画面では「どんな外部アプリ」が「どんな権限」を要求しているのかを明示し、それに対してユーザーが許可の意味で「OK」をクリックするというフローが一般的です。しかし、この認可画面をよく確認せずにクリックしてしまうと、例えばTwitterであれば意図しないツイートを勝手に投稿されてしまう、フォロー・フォロワーを勝手に操作されるなど大変なことになります。

なお余談ですが、認証と認可は厳密にいうと違います。よく「OAuth認証」という用語を目にしますが、OAuthは「認可」のプロトコルであるため混同すると大変な目にあいます[3]

5-4. 透明 iframe

OAuth の認可画面を iframe 内に表示し、iframeを透明にしたり、認可画面であることをわかりにくくするなどのテクニックによって誤操作を誘導する(クリックジャッキングといいます)ということを組み合わせると最初に説明したような XSS ができてしまうことになります。ただ、今では X-Frame-Options などにより対策されていますのでご安心ください。

6. 私、スクリプト実行は onmouseover でって言ったよね!

さて、今までご紹介したまとめとして 2010年頃に Twitter で起きた「マウスオーバー」問題、また別名「RainbowTwtr」をご紹介いたします。Twitter 社の公式発表 では「マウスオーバーの」問題と称していますが、一般には「RainbowTwtr」問題のほうが通りが良いようです。ちなみに、これは「マウスオーバー」自体にセキュリティホールがあったわけではなく、ツイート本文内の URL のサニタイジング漏れによる XSS であり、その一例として「onmouseover="..."」という Proof of concept があっただけの話であり、「マウスオーバー問題」と単純に書かれると誤解を招く恐れがある名前かなと思われます。また、「RainbowTwtr」という名前はこの XSS の Proof of concept を公表した Twitter のアカウント名です。

この XSS には様々な亜種がありましたが、それらの記録をまとめられている方がいましたので、そのうちの一つを引用します[4]


http://a.no/@"onmouseover=";$('textarea:first').val(this.innerHTML);$('.status-update-form').submit()" style="color:#000;background:#000;/


この問題の本質は「@」以降のダブルクォーテーションがエスケープされないというところです。それを利用して、自己言及的な内容を投稿するスクリプトを onmouseover で実行するというスクリプトになっています。本来リンクはクリックという操作をしないとアクションが発生しないというコンテキストがあったにもかかわらず、マウスカーソルをリンクの上にもっていっただけで投稿されてしまう、という新鮮さ(?)が「マウスオーバー問題」という名前になったのかもしれません。

7. XSS は衰退しました(?)

今まで紹介してきた XSS は現代のブラウザや Web サービスにおいては大体対策されてきており、基本的にはそこまで心配する必要のないものです。しかし、形を変えて現代に残っているものもいくつかあります。最後にそういったものをご紹介いたします。

7-1. トラッキングピクセル

Google Analytics など ユーザーのエンゲージメント解析を行うために、img タグでの src 属性にロギング用のエンドポイントを指定することで JavaScript が実行できないような環境においてもトラッキングを可能にするといったことは今でもよく行われています。こういったものを「トラッキングピクセル」と呼びます。例えば現代の HTML メールでは JavaScript の実行はほぼできませんが、このようにトラッキングピクセルを置くことで開封率などのユーザートラッキングが可能になります。メーラーによっては初回受診時に画像を表示するか否かを確認されるのは、メールの送信者に対してこのようなトラッキングを許可するか?といった確認の意味も含まれています。また、Google が推進する AMP などでは専用のタグ[5]が用意されていたりします。

7−2. スーパークッキー

現代のブラウザは HTTP Strict Transport Security (HSTS) といわれる機能を実装しています。これは、簡単にいうと HTTP でアクセスしてきたクライアントに対して、次回から HTTP「S」の使用をユーザーに要求するためのプロトコルです。しかしこの仕様を用いることでブラウザのページ内に閉じたセッションを越えてユーザーをトラッキングすることが可能になってしまうスーパークッキーという方法が提唱されています[6]

原理としては、HSTS により HTTP「S」でのアクセスが要求されたブラウザプロセスは、たとえそのサイトを抜けたとしてもその後常に指定された ドメイン に対しては HTTP「S」でアクセスするようになります。これを用いると、あるドメインに対して、HSTS の要求を受けたブラウザと受けていないブラウザの2種に分類することができるのです。これをビットとして考えると30個程度のドメインを用意するだけでクロスドメイントラッキングが可能になるのです。


<img src="http://evil-site1.jp/"> <!-- HSTS の要求が以前にあったので HTTPS で接続-->
<img src="http://evil-site2.jp/"> <!-- HSTS の要求が以前にあったので HTTPS で接続-->
<img src="http://evil-site3.jp/"> <!-- HSTS の要求なしなので HTTP で接続-->
<img src="http://evil-site4.jp/"> <!-- HSTS の要求なしなので HTTP で接続-->
<img src="http://evil-site5.jp/"> <!-- HSTS の要求が以前にあったので HTTPS で接続-->
<img src="http://evil-site6.jp/"> <!-- HSTS の要求が以前にあったので HTTPS で接続-->
<img src="http://evil-site7.jp/"> <!-- HSTS の要求が以前にあったので HTTPS で接続-->
...


2−3 で説明した SNS の足跡機能を悪用したものと発想は近いものですね。

8. 終わりに

本記事は NIKKEI Advent Calendar 5日目の記事として、いにしえのXSS[2] と現代における発展についてご紹介させていただきました。

テクノロジーメディアを目指す日本経済新聞社では、最新のセキュリティ技術を日々キャッチアップし、セキュアなサービスを目指して日々開発しております。 セキュリティの技術を生かしてメディアの未来を作る仕事に興味のある方は、ぜひお気軽にご連絡ください。

https://hack.nikkei.com/jobs

明日は6日目、中川さんによる「在宅勤務時のチーム交流の工夫」です。お楽しみに!

注釈

[※1]: ダイヤルアップ接続のインターネットなど、いにしえの時代を懐かしむ人たちを指すネットスラング。 [ピクシブ大百科:インターネット老人会](https://dic.pixiv.net/a/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%8D%E3%83%83%E3%83%88%E8%80%81%E4%BA%BA%E4%BC%9A)

[※2]: 実際はXSSではなくCSRFであったり、そもそも「スクリプティング」ですらなかったり、単にHTTPプロトコルの悪用だったりと厳密なXSSの定義には当てはまらないものもありますが、本記事では便宜上XSSと呼んでいます。

[※3]: [「単なるOAUTH 2.0を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる」について](https://tech-lab.sios.jp/archives/13002)

[※4]: [twitter XSS脆弱性の観測記録](https://agano-2.hatenadiary.org/entry/20100922/p1)

[※5]: [amp-pixel](https://amp.dev/ja/documentation/components/amp-pixel/?format=websites)

[※6]: [プライベートモードのブラウザーでも利用者を特定できる「スーパークッキー」](https://project.nikkeibp.co.jp/idg/atcl/idg/14/481709/011300055/?ST=idg-cio-consumerit&P=3)

淵脇誠
ENGINEER淵脇誠

Entry

各種エントリーはこちらから

キャリア採用
Entry
新卒採用
Entry
短期インターン
Entry