この記事は IPFactory Advent Calendar 2022 の 8 日目の記事です。
LTネタの消化
こんにちは。y0d3nです。
先日サークル内で行われた LT大会で喋ったネタをブログにすることで Advent Calendarの枠を埋める作戦です。
まずは前提知識として Origin と SOP の簡単な概要について触れた後、それから思いついたことの検証の結果について述べます。
Origin
Webセキュリティを考えるうえでめちゃめちゃ重要な概念として挙げられる Origin 。
Origin はURL のスキーム (プロトコル)、ホスト (ドメイン)、ポートによって定義され、それら 3 つが全て一致したときのみ同一オリジンとして扱われます
https://example.com |
オリジン判定 |
---|---|
https://example.com |
同一 |
https://example.com/hoge/fuga |
同一 |
https://example.com:443 |
同一 |
https://example.com:8080 |
異なる *1 |
http//example.com |
異なる |
http//www.example.com |
異なる |
ただ、Origin という概念があるだけでは仕方ありません。
Origin 単位でコンテンツを分離するセキュリティ機構として、SOPがあります。
Same Origin Policy
あるオリジンによって読み込まれた文書やスクリプトが、他のオリジンにあるリソースにアクセスできる方法を制限するセキュリティ機構です。
これだけでは少々わかりにくいので、図示してみましょう。
登場人物(?) は http://secret.com
と http://attacker.com
です。
それぞれ以下のようになっていると考えましょう。
http://secret.com
: 利用者本人にしか開けないサイトhttp://attacker.com
: 攻撃者の用意した罠サイト
SOPがないWebでは以下のようなことが可能になってしまうことが考えられます。
- 利用者が罠リンクか何かを踏んで
http://attacker.com
にアクセスする http://attacker.com
のレスポンスとして、移行の手順を自動で行うスクリプトが返ってくるhttp://secret.com
にアクセスするhttp://secret.com
のレスポンスとして、利用者の秘密情報が返ってくる- 秘密情報を
http://attacker.com
に送信する
こんなことができてしまったら、気軽にネットサーフィンもできませんね。
しかし、SOPがあることで、http://attacker.com
を生成元とするスクリプトは http://secret.com
のレスポンスを取得できなくなります。(リクエスト自体は送信できる(実際は少し制限があるけど今回は気にしない))
SOP を利用することで安全にネットサーフィンができます。
Origin の継承
さて、ここからが本題です。
about:blank
やjavascript:
のURLから実行されたスクリプトは、そのURLを開いた Origin を継承します。
例えば、以下のようにした href
の中には明示的なオリジンがありません。
<a href="javascript:alert(document.domain)">alert</a>
javascript
スキームでは Origin が継承されるため document.domain
はその時に開いているドメインとなります。
他のパターンも見てみましょう。
いくつかピックアップします。
http://example.com/...
のように明示的なドメインが設定されている場合は Origin が新しいものに切り替わるdata:
スキームでは "空"(empty) になるfile:
スキームでは "不透明"(opaque) と表現されている
opaque というのは null
でシリアル化された Origin のようです。
file:
を使ってローカルファイルをブラウザで開いた際は opaque と呼ばれる特徴的な Origin になるらしいことがわかります。
Opaque
file:
を使用してローカルファイルを開いた時のことを想像してみます。
- 適当なパスを指摘してファイルを開くことができる
- それらは全て
null
でシリアル化された Origin がセットされる
ここで「ローカルにダウンロードした悪意あるHTMLをブラウザで開いた際、ローカルの任意ファイルが同一オリジンとして扱われてしまうのでは?」という考えがよぎりました。
ダウンロードした得体の知れない exe は流石に開きませんが、htmlくらいだったらつい開いてしまいそうです。
試してみましょう。
「ダウンロードしたHTMLをブラウザで開く」という状況なので、ダウンロードフォルダに適当なHTMLファイルを作成します。
iframe
で secret.txt
を読み込み、それを JS で盗むという流れを試してみます。
<body> <script> var iframe = document.createElement('iframe'); iframe.onload = function () { alert(document.getElementsByTagName("iframe")[0] .contentWindow.document.documentElement.innerHTML); }; iframe.src = './secret.txt'; document.body.appendChild(iframe); </script> </body>
ちゃんと、"null" の Origin からは cross-origin frame にアクセスできないようになっていました。
some browsers
このリンクは先ほども貼りましたが、一部引用します。
Note that the URL specification states that the origin of files is implementation-dependent, and some browsers may treat files in the same directory or subdirectory as same-origin even though this has security implications.
「... some browsers may treat files in the same directory or subdirectory as same-origin ...」の部分が興味深いですね。
こういう時の「some browsers」で想像するブラウザはなんでしょうか。
はい。IEですね。
Edge の IE モードを使用して試してみます。
secret.txt の中身にアクセスできました。
さて、先ほど引用した mozilla の文章には「same directory or subdirectory」とありましたが、それも怪しいです。
任意ファイル開けるかな~と思って file:///C:/Windows/win.ini
を開こうとしたら iframe で読み込んだ時点でダウンロードをしようとして来て焦りました。
ActiveXObjectを利用してレスポンスを取得し、alert するように書いてみました。
<body> <script> var httpReq = new ActiveXObject('Msxml2.XMLHTTP.6.0'); httpReq.onreadystatechange = cons; httpReq.open('get', "file:///C:/Windows/win.ini", true); httpReq.send(null); function cons(e) { alert(httpReq.responseText) } </script> </body>
ローカルの悪意あるHTMLファイルを開いただけで任意ファイルにアクセスできることが確認できました。
先ほども述べましたが、「exe とかは流石に開かないよ」って人もhtmlだったら平気で開きそうなので怖いですね。
IE等を使わないと再現しないため現代ではほぼ気にしなくてよさそうですが、他にもセキュリティリスクが眠っていることがありそうですのでダウンロードしたファイルは気を付けるようにしたいですね。
オリジン、とてもよくできていて関心することばかりです。
最後までご覧いただきありがとうございました。
この記事は IPFactory Advent Calendar 2022 の 8 日目の記事です。
昨日は HARU くんによる「ゲーム制作における画像素材の大変さ」でした。
作ったゲームを学校の文化祭で展示したり、他にもいろいろゲーム作ったりしていてすごいこです。ぜひご覧ください。
学園祭にて『🔑謎解き脱出ゲーム🐍』を制作しました!!上位13名には景品も用意しているのでぜひお気軽にプレイしてみてください!
— HARU (@iscsec_haru) 2022年10月29日
現在oViceにて配信中です。10月30日には605教室でもプレイ出来ます。 pic.twitter.com/59OA2IN2xA
さて、明日の枠が埋まっていません。命のリレーは途切れてしまうのでしょうか。
そんなことを言っていたらまたまた HARU くんが命のリレーを引き継いでくれました。 たのしみですね