HOME情報セキュリティ資料・報告書・出版物調査・研究報告書情報セキュリティ技術動向調査(2011 年上期)

本文を印刷する

情報セキュリティ

情報セキュリティ技術動向調査(2011 年上期)

5. Ajaxブラウザセキュリティ - 主戦場をDOMに移したXSS

長谷川 武

5.1. 概要

 この何年かでWebアプリケーションは、サーバ上のみでプログラムが動作するものから、多くのJavaScriptコードがWebブラウザで動作し、背後でサーバと連携して、リッチかつスムーズな操作感を提供するものへと進化してきた。このことは、Webブラウザ上に悪意のスクリプトを送り込む「スクリプト注入(XSS)」攻撃を、より防止しづらく、悪用の懸念がより大きなものに変化させた。本稿は、WebアプリケーションにAjax等の進化をもたらした技術について振り返るとともに、スクリプト注入対策を考慮すべき新たな文脈について論じるものである。

5.2. Ajaxの登場と進化

 Ajax(Asynchronous JavaScript and XML)[1]は、必要となるデータを非同期でWebサーバから取り寄せつつ、Webブラウザの画面上でなめらかな操作性を提供する形態のJavaScriptプログラムのことである。かつてのブラウザにおいてはこのようなことはできなかったが、ある時点からWebブラウザが装備するJavaScript向けAPIが進化し、可能になった。この、JavaScriptの中からのWebサーバとの間の非同期の通信を可能にしたAPIは、XMLHttpRequestという組み込みオブジェクトである(文書上においてはXHRと略されることもある)。最初Internet Explorerに登場し、やがて他のブラウザにも広まっていった。現在XMLHttpRequestオブジェクトには「レベル2」と呼ばれる仕様のものが登場し、さらなる進化を続けている。
 図1に、主なブラウザがXMLHttpRequestをサポートした年次を示す。

図1:JavaScript向けに非同期通信APIが登場

5.3. Ajax以前 - スムーズでない操作性

 Ajaxの登場以前のWebアプリケーションは、ユーザにとって、必ずしもスムーズとは言えない操作性のものだった。
 ブラウザの画面でボタンやリンクをクリックすると、Webサーバから応答が返されて画面が再描画されるまで、ユーザは待たされる。表示される情報の変化がごく小さいものであっても、画面(もしくはひとつのフレーム)全体の書き換えが必要だった。プログラマにとっても自由度が低かった。ブラウザ上でJavaScriptプログラムを動作させることはできたが、JavaScriptプログラムからWebサーバへリクエストを発信すると、その時点でJavaScriptプログラムは終了してしまう。呼び出した新しいWebコンテンツに場所を明け渡さなくてはならなかったのである。

5.4. Ajaxの始まり

 上記の不便は、JavaScriptに非同期通信を許すXMLHttpRequestオブジェクトの登場によって(すべてではないが大幅に)解消された。JavaScriptプログラムを終わらせなくても処理の途中でWebサーバへデータを要求できるようになったことの恩恵は大きい。DOM(Document Object Model)のAPIを使って画面の一部分のみ表示内容を変更するテクニックが以前から使えたが、これをWebサーバとの非同期通信と組み合わせられるからである。画面の背後で必要なデータのみをサーバから受け取り、画面の再描画を最小限の範囲で行うことによって、なめらかな操作性が実現する。

5.5. 「同一源泉」の制約

 当初のXMLHttpRequestには、セキュリティへの配慮から、"Same Origin Policy"(同一源泉ポリシ)と呼ばれる制約が課されていた。XMLHttpRequestオブジェクトを使ってリクエストを送信できる先は、現在のWebコンテンツの出身のWebサーバに限る、というものである。

5.6. XML / JSON

 Ajaxは、その名の由来が示すとおり、非同期でWebサーバから受け取るレスポンスのデータフォーマットにXMLを用いることが当初想定されていた。しかし、XMLにはデータの記述に比較的多くの文字数を要し、Webサーバとのデータの送受信にはXMLのもつ高度な記述能力は必ずしも必要とされない。最近ではXMLに代わり、文法がより単純で記述のための文字数も少なくて済む、JSON(JavaScript Object Notation)と呼ばれるフォーマットが使われるようになってきた。
 その名のとおりJSONは、JavaScriptと縁が深い。JSONは、JavaScriptのソースコード内でオブジェクトの値を表す構文を、プログラム外でデータ交換するためのフォーマットに転用したものである。
 JavaScriptの規格であるECMAScript edition 5(2009年12月)においては、JSON文字列データとJavaScriptオブジェクトとの間の相互変換のAPIが用意された。

  • JSONで扱えるデータ型
  •   ・数値      1701  -2.78 3.6e-12
      ・文字列    "hello"  "?""
      ・真偽値    true  false
      ・ナル      null
      ・配列      ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
      ・ハッシュ  {"name": "浦島太郎", "age": 256, "phone": null, "real": false}
    	
  • JSONによるデータ構造記述例
  •   {"books": [
          {"subject": "Rama II", "authors": ["Clarke", "Lee"], "issued": "1 Nov 1990"},
          {"subject": "Orion", "authors": ["Kirk", "Scott"], "issued": "1 Apr 2264"},
          {"subject": "Android", "authors": ["Data", "Laforge"], "issued": "stardate 47232.1"}]}
    	

5.7. マッシュアップ時代の訪れ

 最近は、既存のWeb API(Webサービス)を積極的に利用して作られる「マッシュアップ」タイプのWebアプリケーションが増えてきた。マッシュアップタイプのWebアプリケーションとは、外見は一貫性のあるデザインをもち一枚岩のように見せつつ、内部は、異なる主催者が提供するWeb API(Webサービス)を(場合によっては複数)組み合わせて作られるものを指す。この方法をとることによって、既存のWebサイトにさらなる付加価値を付ける別のサイトを短期間で立ち上げられる等のメリットがある。

5.8. クロスドメイン呼出しの必要性と危険なJSONP

 Ajaxの形態でマッシュアップタイプのWebアプリケーションを構築しようとする際に邪魔になるのがXMLHttpRequestの"Same Origin Policy" だ。従来のXMLHttpRequestにおいては、HTTPリクエストを送ることが可能な先は、自分の「出身地」のWebサーバのみに限られるからである。現在いくつかのブラウザにおいては、XMLHttpRequestレベル2がサポートされ、「出身地」とは異なるドメインのWebサーバへもリクエストを送れるようになった(クロスドメイン呼び出し)[2]。しかし、2009年になるまでそれはできなかった。そこで、一部のプログラマたちはXMLHttpRequestを使わずして非同期通信を行う手法を見つけ、利用してきた経緯がある。そのひとつがJSONP(JSON with Padding)[3]と呼ばれる手法だ。JSONPによるクロスドメインの非同期通信は次のように行われる。

    (1) <script src="uri">タグをDOMへ動的に追加して、他ドメインのWebサーバへス
     クリプトのコンテンツを要求する形のWeb API呼び出しを行う。このときのuri
    には、当該APIに引き渡す必要のあるパラメータを含めておく

    (2) そのAPIが返すレスポンスは、次のような関数呼び出しの形のスクリプト文字列になるようにする
       callback_func("JSON形式データ")

    (3) 呼び出し側に callback_func()関数の実体を用意しておき、Webサーバから返され
     た"JSON形式データ"を処理するようにする。なお、callback_funcの関数名は、
     (1)の段階で例えば、?callback=callback_func のようなパラメータでAPI側へ受
     け渡すことが多い

 レスポンスにJSON形式データのみならず、それを処理する関数呼び出しも「追加の詰めもの」(padding)として含める形をとることからJSONPの呼び名ができた。この方法は任意のサーバとのやりとりができて便利である反面、相手が悪意あるサーバの場合、ブラウザを侵害するスクリプトが送られてきて実行されるという、大きなリスクを抱えている。

5.9. XHRレベル2

 2009年になって、Firefox, Google Chrome, Safari, IE 8等のブラウザが相次いで、XMLHttpRequest レベル2をサポートし始めた(IE 8においてはXDomainRequestというオブジェクト)。XMLHttpRequestレベル2においてはクロスドメイン呼び出しが解禁された。ただし、無差別にクロスドメイン呼び出しを許すのは危険であるので、同時に、呼び出しの許可や制限を行うためのAccess-Control-Allow-Origin等のレスポンスヘッダが導入された。
 例えば、次のようなレスポンスヘッダをWebサーバが返してくる場合、

    Access-Control-Allow-Origin: 呼び出し側ドメイン

 ここに示された 呼び出し側ドメイン に該当する「出身地」のコンテンツからは当該サーバへクロスドメイン呼び出しが許され、他は許されない。悪意あるマッシュアップサイトが善意のWeb APIサイトを悪用してユーザに詐欺をはたらく、といったケースはこの制約である程度防げると期待できる。お気づきのとおり、これらのレスポンスヘッダはWeb API側が「呼び出し元」に対して制約をかけるものである。呼び出し側が「呼び出せる先」の範囲を制約するような仕組みはいまのところ用意されていない。呼び出し側が何らかの干渉を受け、悪意のWeb APIを誤って呼び出すよう仕向けられた場合には、被害が生じるおそれがある。

5.10. 新しいタイプのスクリプト注入(XSS)攻撃

 Ajaxアプリケーションにおいては当然のことながらJavaScriptが多用される。ユーザに提供する機能が高度になるにつれ、使われるJavaScriptコードも大規模かつ複雑になってきている。そのようなWebコンテンツの中へひとたび「悪意のスクリプト」が紛れ込めば、機密情報の漏えい、重要なデータの改ざん、業務妨害をはじめとする、望ましくない事態の発生が危惧される。しかも、悪意のスクリプトがコンテンツに紛れ込む手口は従来のスクリプト注入(XSS)攻撃とは事情が違ってきている。Ajaxアプリケーションに悪意のスクリプトが侵入する攻撃は、概ね次のいずれかの形で起こる。

    (1) 信頼できない第三者スクリプトのロード。第三者のWebサイトから取り込んだ
        JavaScriptプログラムに悪意のスクリプトが潜んでいる

    (2) JSONPによるスクリプト侵入。JSONPを用いた第三者のWeb API呼び出しへの
        応答として悪意のスクリプトが返される

    (3) 従来型のスクリプト注入(XSS)。サーバ側プログラムの不備によって、Webペー
        ジ内に悪意のスクリプトが侵入する

    (4) DOMベースのスクリプト注入(XSS)[4]。ブラウザ上のJavaScriptプログラムが、
        DOM上の危険な入力地点(ソース)から得たデータに含まれる攻撃パターンをそ
        のままにして危険な出力地点(シンク)へ送り出すことで、Webページ内に悪意
        のスクリプトが侵入する

5.11. 従来型のスクリプト注入(XSS)攻撃

 従来型のスクリプト注入攻撃は、HTMLのタグの中に<script>...</script>のようなスクリプトタグを紛れ込ませて悪意のスクリプトを実行させるものだった。
 図2に従来型のスクリプト注入(XSS)攻撃の図式を示す。ここでは、サーバ側プログラムの「ソース」から入ってくる攻撃パターンを見過ごして「シンク」へそのまま出力することで攻撃が成立する。したがって対策は、「シンク」に危険な内容が出力されないよう、サーバ側プログラムの中で特殊記号を無害化する等、比較的単純なもので済んでいた。

図2: 従来型のスクリプト注入(XSS)攻撃

5.12. DOMを舞台とするスクリプト注入(XSS)攻撃

 DOMを舞台とするスクリプト注入(XSS)攻撃においてはだいぶ事情が違ってきている。Ajaxタイプのプログラムの動作は、ブラウザ上で動くJavaScriptによって動的にDOMが書き換えられつつ進行する。そのため、Webページ内への不正なスクリプトの侵入は、ブラウザ上のJavaScriptプログラムが、DOM上の危険な入力地点(ソース)から得たデータに含まれる攻撃パターンをそのままにして危険な出力地点(シンク)へ送り出すことで、Webページ内に悪意のスクリプトが侵入する形をとる[4]

図3: DOMを舞台とするスクリプト注入(XSS)攻撃

 マッシュアップ・アプリケーション(他者が用意したWeb APIを積極的に利用するスタイルのアプリケーション)のばあい、第三者のWebサービスからデータやスクリプトを受け取る場面で、予期せぬ攻撃や脆弱性を呼び込むおそれがある。

5.13. DOM上の警戒すべき入力地点(ソース)

 DOMベースのスクリプト注入(XSS)に関して警戒すべき入力地点(ソース)には、例えば、次のものが考えられる。

  (1) コンテンツをロードした際のURIの構成要素
  • document.URL
  • document.location
  • location.pathname
  • location.search
  • location.hash
(2) コンテンツのURIが書かれていたリンク元ページのURI
  • document.referrer
(3) Cookie
  • document.cookie
(4) ウィンドウ間/フレーム間の値受渡しに使われる変数
  • window.name
  • postMessage(arg) 受信側の関数引数 f(arg) のarg.data

5.14. DOM上の警戒すべき出力地点(シンク)

 DOMベースのスクリプト注入(XSS)に関して警戒すべき出力地点(シンク)には、例えば、次のものが考えられる。

  (1) 信頼できない第三者スクリプトを含むおそれのあるコンテンツのロード
  • iframe.src = html-uri
  • script.src = script-uri
(2) 文字列がスクリプトとして解釈実行される文脈への値の送り込み
  • script.innerHTML = code-string
  • eval(code-string)
  • setTimeout(code-string, msec)
  • window.execScript(code-string)   // Internet Explorer固有
(3) <script>タグが効力をもつ文脈への値の出力
  • document.write(text)

5.15. DOM XSS対策

 DOMベースのスクリプト注入(XSS)対策としてクライアント側で動作するプログラムの中で次の事項を行う。

 

(1) 十分信頼できるWebサイト以外からJavaScriptファイルをロードしない。

(2) 十分信頼できるWebサービス以外をJSONPで呼び出さない。

(3) JSON形式文字列データをJavaScriptオブジェクトに復元するには、eval()関数
    ではなくJSON.parse()メソッドを使う。

(4) eval()関数をはじめとする、文字列がスクリプトとして解釈実行される文脈へ送り
    出す値については次のようにする。
    a)  原則として、警戒すべき入力地点(ソース)から得た値を渡さない。
    b)  他に方法がなく、警戒すべき入力地点(ソース)から得た値を渡さざるを得な
        い場合、実行されることになるスクリプト文字列をチェックし、望ましい振
        る舞いをすることが確認できたもののみを渡す。
(5) document.write()に渡す値は、常に定番のサニタイズ処理を施してから引数へ渡す。
    a)  < → &lt;
    b)  > → &gt;
    c)  " → &quot;
    d)  ' → &#39;
    e)  & → &amp;
(6) 警戒すべき入力地点(ソース)から得た値はどれも、その項目の所定の仕様を満
    たしているか厳しくチェックする。例えば次のような観点でチェックする。
    a)  文字種
    b)  桁数の範囲
    c)  数値の範囲
    d)  特定のリストに含まれる 等

上記に加えて従来型のスクリプト注入対策も、もちろん、必要である。

 

(7) サーバ側プログラムにスクリプト注入(XSS)対策を施す。

5.16. ブラウザにおけるXSS対策

 ブラウザベンダによるスクリプト注入(XSS)対策も進められている。ここでは主なものをふたつ紹介する。

  (1) X-Content-Security-Policy
    そのひとつは、Firefox 4以降に導入された、Content Security Policyと呼ばれる対策機能である。X-Content-Security-Policyレスポンスヘッダによって活性化される。
(2) X-XSS-Protection(XSS Filter)
    もうひとつは、Internet Explorer 8以降に導入された、XSS Filterと呼ばれる対策機能である。X-XSS-Protectionレスポンスヘッダによって活性化される。

5.17. X-Content-Security-Policy

 Content Security Policyは、Firefox 4以降(厳密にはHTMLレンダリングエンジンGecko 2.0以降を使用するブラウザ)が備えるスクリプト注入(XSS)対策機能である[5][6]
 これは、ブラウザがどこからスクリプトをロードするかについて、整然と制約を設けることによって、信頼できないサイトからのスクリプトの混入を防ごうというものである。
この対策機能は、

    X-Content-Security-Policy: 仕様
の形のレスポンスヘッダをFirefoxが受信すると活性化される。Webコンテンツ内のスクリプトの配置の自由度は減るが、どのスクリプトが実行されどれが禁止されるかを客観的に把握しつつ対策を行えるのが特長である。

 この仕様には複雑な指定が可能であるが、最も単純なものは、例えば、

    X-Content-Security-Policy: allow 'self '
のような指定である。これによって、ブラウザにおけるスクリプトの実行が次のように制約されるようになる:

  • HTMLの途中に次のような形で記述する、いわゆる「インライン」のスクリプトは実行が禁止される:
    • ...<script>スクリプトソースコード</script>...
  • 動作させたいスクリプトは原則として、次の形で独立した別のコンテンツとして呼び込まなくてはならない:
    • <script src="スクリプトuri"></script>
    このスクリプトuriとして許されるのは、このWebページの源泉と同じサーバに限られる。
  • 文字列をスクリプトのソースコードとして解釈実行するeval()等の関数の実行が禁止される。

 なお、X-Content-Security-Policyレスポンスヘッダは、多くのパラメータもっていて、よりきめ細かな指定をすることができる。

 
X-Content-Security-Policy: allow ホスト...
   [; options {inline-script | eval-script}...]
   [; img-src ホスト...][; media-src ホスト...]
   [; srcipt-src ホスト...][; object-src ホスト...]
   [; frame-src ホスト...][; font-src ホスト...]
   [; xhr-src ホスト...][; frame-ancesors ホスト...]
   [; style-src ホスト...]
   [; report-uri uri][; policy-uri uri]

  • ホスト名を具体的に羅列することによって、Webページの源泉以外のサーバからのスクリプトのロードを許可/限定できる:
    • script-src ホスト...
  • スクリプトのロード元を許可するのと同様の要領で、画像等各種リソースのロード元のホストを明示できる:
    • img-src ホスト...; media-src ホスト...; style-src ホスト...; ...
  • Webページの源泉のホストにからの追加リソースのロードを禁止することもできる
    • allow 'none' [別のホスト...]
  • せっかくのインラインスクリプトやeval()の禁止機能であるが、その禁止を解除することもできる
    • options inline-script eval-script
  • 現在はまだ報告される項目がごく少ないようであるが、指定されたホストにエラー報告を行うような機能も備わる模様である:
    • report-uri uri

5.18. X-XSS-Protection(XSS Filter)

 XSS Filterは、Internet Explorer 8(以下IE 8)以降が備えているスクリプト注入(XSS)対策機能である[7][8]。類似の対策機能は、Google ChromeやSafariの新しいバージョンにも備わっている。
 これは、HTTPリクエストに含まれていたスクリプト注入(XSS)攻撃パターンがサーバからそのままHTTPレスポンスに出力されてきたら、そのスクリプトを動作させないというものである。この対策機能は、例えば、

     X-XSS-Protection: 1
のようなレスポンスヘッダをIE 8(およびそれ以降のバージョン)等が受信すると活性化される。
 このXSS FilterがHTTPレスポンスに対して防御効果を発揮する条件はつぎのようなものである:

  (1) 機能が活性化する条件
  次のいずれかの指定/設定によって、XSS Filter機能がオンになる
  • HTTPレスポンスに次のいずれかのレスポンスヘッダが存在する
    • X-XSS-Protection: 1 または
      X-XSS-Protection: 1; mode=block
  • HTTPレスポンスにX-XSS-Protectionレスポンスヘッダが存在せず、「インターネットオプション」の「XSSフィルターを有効にする」項目が「有効」に設定されている
    • 操作要領:
        コントロールパネル > インターネットオプション >
        セキュリティ > 該当するゾーン > レベルのカスタマイズ >
        XSSフィルターを有効にする > 有効にする
      これはIE 8(およびそれ以降のバージョン)についてのみ言えることで、Google ChromeとSafariには上記のようなデフォルト設定の切り替え機能はない。Google ChromeとSafariの新しいバージョンにおけるXSS Filter機能のデフォルトは常に「オン」になっている。
HTTPレスポンスにX-XSS-Protection: 0レスポンスヘッダがあると、「インターネットオプション」の「XSSフィルターを有効にする」項目よりも優先される。このヘッダがあると、XSS Filter機能はオフになる
 
  (2) 検出/防御してくれるもの
  次の両方が成り立つと判定したとき、XSS Filterは防御効果を発揮する
  • HTTPレスポンスの元となるHTTPリクエストに、スクリプト注入(XSS)攻撃パターンと見なされるものが含まれていて、それと同じものがHTTPレスポンスにも出現している
  • HTTPレスポンスの元となるHTTPリクエストを発生させたアクションやリンクは、HTTPレスポンスを返したものとは異なるサイトから来たものである
  • 同一のサーバでも、ホスト名による参照とIPアドレスによる参照は異なるサイトとみなされる

例えば、次のようなリクエストデータ、

    <script>alert(1)</script>

をHTTPレスポンス内にエコーさせると、ブラウザのHTMLソース上にはそのまま置かれるが、注入されたスクリプトは実行されない。
 また、次のようなリクエストデータ、

    <img src="x" onerror="alert(1)">

をHTTPレスポンス内にエコーさせると、次の、

    <img src="x" #nerror="alert#1#">

のように書き換えられ、HTMLソースの記述上も注入されたスクリプトが無効にされる。
 次のレスポンスヘッダ、

    X-XSS-Protection: 1; mode=block

が指定されたときに、攻撃パターンがあると判定されると、IE8(及びそれ以降)においては、HTTPレスポンスのコンテンツ全体が

    #

ただひとつに置き換えられる。Google ChromeとSafariにおいては、空白のページが表示される。

注意すべき点として、このXSS Filterでは、次のものを阻止できないことがあげられる。

  • document.write()によるページ内への<script>...</script>タグの挿入
  • DOMを通じた<script>タグのスクリプト内容の書き換え(Google ChromeやSafariにおいて)
  • JSONP方式によるスクリプトの無防備な取り込み 等
DOMベースのXSSへの対策としては課題を残している。

5.19. まとめ

 Ajaxの進歩とマッシュアップ開発スタイルの発展に伴い、Webアプリケーションのブラウザ側のプログラミングは複雑になった。Webコンテンツの中への悪意のスクリプトの侵入の手口も多様化している。かつては、予定外のスクリプトが入り込まないよう、Webページの保護につとめればよかった。しかし今や、クライアント側の、DOMから値を取り出す箇所、DOMの一部を書き換える箇所のそれぞれにおける警戒と対策が必要である。攻防は、いわば「DOMがかたちづくるジャングル」の中のゲリラ戦に移ってきたといってよい。
 その一方、ブラウザのスクリプト注入(XSS)対策機能も進歩してきている。Firefox 4 [9]以降がもつContent Security Policyは、インラインスクリプトや他ホストからのスクリプトのロードを抑制でき、DOMベースのスクリプト注入(XSS)に効果を発揮する。
 Internet Explorer 8 [10]以降(およびGoogle Chrome[11]、Safari[12]の新しいバージョン)がもつXSS Filterは、DOMベースのスクリプト注入(XSS)対策には不足があるものの、有用な対策機能のひとつである。

以上

参考文献

Ajax
[1] "Ajax - Wikipedia"
http://ja.wikipedia.org/wiki/Ajax
[2] "Cross-Origin Resource Sharing - Wikipedia, the free encyclopedia"
http://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing
[3] "JSONP - Wikipedia“
http://ja.wikipedia.org/wiki/JSONP
 
DOMベースのスクリプト注入(XSS)
[4] "DOM Xss Identification and Exploitation“
http://dominator.googlecode.com/files/DOMXss_Identification_and_exploitation.pdf
 
Content Security Policy - Firefox(Gecko 2.0)のXSS対策機能
[5] "Introducing Content Security Policy - MDC Docs"
https://developer.mozilla.org/en/Introducing_Content_Security_Policy
[6] "Using Content Security Policy - MDC Docs"
https://developer.mozilla.org/en/Security/CSP/Using_Content_Security_Policy
 
XSS Filter - IE 8以降、およびGoogle Chrome、SafariのXSS対策機能
[7] "IE8 Security Part IV: The XSS Filter"
http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx
[8] "IE8 XSS filter: what does it really do?"
http://stackoverflow.com/questions/2051632/ie8-xss-filter-what-does-it-really-do
 
ブラウザのバージョン履歴
[09] "Old Version of Firefox 1.0 Download - OldApps.com"
http://www.oldapps.com/firefox.php?old_firefox=48
[10] "History of Internet Explorer - Wikipedia, the free encyclopedia"
http://en.wikipedia.org/wiki/History_of_Internet_Explorer
[11] "Old Version of Google Chrome Download - OldApps.com"
http://www.oldapps.com/google_chrome.php
[12] "Safari version history - Wikipedia, the free encyclopedia"
http://en.wikipedia.org/wiki/Safari_version_history

 

目次へ 次へ