全ページに SAMEORIGIN を付けようとして止まった
ウェブの応答に X-Frame-Options というヘッダを付けると、ほかのサイトの iframe に自分のページを埋め込ませないようにできます。悪意のあるページが透明な iframe で他サイトの画面を重ね、利用者に意図しないクリックをさせる。そんなクリックジャッキングへの、よくある固めの一つです。
私たちもこれを入れようとしました。値は SAMEORIGIN で、全ページに一律に付ける方針です。手間が少なく、書き漏らしも出にくい。一番簡単に見える方法でした。
ところが、ここでいったん手を止めました。理由は、私たち自身のスマホアプリにあります。
このアプリは、一部の画面でサーバの HTML を、アプリの中の iframe で開いています。WebView を使うハイブリッドのアプリでは、アプリ自身の画面のオリジンが、サーバのドメインとは別になります。SAMEORIGIN は「埋め込む側と埋め込まれる側のオリジンが同じときだけ許す」という指定です。つまり、一律に付けると、自分のアプリが自分のページを開く表示まで「よそからの埋め込み」として扱われ、弾かれてしまいます。弾かれた画面は、白くなります。
クリックジャッキング対策が目的なのに、自分の画面を壊しては意味がありません。そこで、入れる前に実際に起きるかどうかを確かめることにしました。
実ブラウザで確かめたら、画面が真っ白になった
頭の中の理屈だけで「たぶん壊れる」と言っても、判断の材料としては弱いと考えました。そこで実ブラウザで確かめました。
やり方はこうです。アプリ側と同じオリジンの親ページを用意し、そこから本番の画面を iframe で開きます。アプリが実際に画面を開くときの関係を、ブラウザの上で再現した形です。
| 条件 | 結果 |
|---|---|
| ヘッダなし | 中身が 3972 字読めた |
| 応答に SAMEORIGIN を 1 行足した | 同じ画面が真っ白になった |
差はヘッダ 1 行だけです。画面も、親ページも、開き方も変えていません。それでも、読めていた 3972 字が、まるごと消えました。
このまま一律に入れていたら、アプリの中の画面が白くなるところでした。確かめずに「よくある固めだから」と入れていたら、気づくのは利用者の画面が白くなってからだったかもしれません。ここは、入れる前に実ブラウザで見ておいて良かった点です。
埋め込みで使う文書だけを対象から外した
直し方は単純です。アプリが埋め込んで使う文書だけを対象から外し、ほかは全部に付けるようにしました。
手間がかかったのは、外す対象を決める部分です。「全部に付ける」の前に、自分のアプリが自分のどのページを埋め込んでいるかを洗い出す必要があります。これはアプリのコードをたどって調べました。埋め込みで開くものと、外のブラウザで開くものを分け、それぞれ確かめています。
この分け方は大事です。外のブラウザで開くページは、iframe の中には入りません。だから SAMEORIGIN を付けても壊れません。壊れるのは、アプリの中の iframe で開くものだけです。ここを混ぜてしまうと、外すべきでないページまで外してしまいますし、逆に外し忘れれば画面が白くなります。
入れたあとは、二つのことを確かめました。
- 実ブラウザで、アプリの画面がそのまま映ること。
- 固めが効いていること。これはヘッダを見て確かめました。
確かめの項目は 35 項目ありました。結果は、すべて通りました。
そこから決めたこと
ヘッダの固めは、「全部に付ける」が一番簡単に見えます。私たちもそう思って始めました。ただ、自分たちのアプリが自分たちのページを埋め込んでいる所があると、そこが壊れます。
だから、次のように決めました。付ける前に、埋め込み先を探します。そして実ブラウザで、画面が白くならないかを見ておきます。この二つを、固めを入れる手順の一部にします。
この話は X-Frame-Options に限らないと考えています。ほかのサイトから守るための設定は、自分のアプリやシステムも「ほかのサイト」と同じ扱いにしてしまうことがあります。ハイブリッドのアプリのように、自分の画面のオリジンが自分のサーバと別になる作りでは、特にそうです。守る側の設定を足すときは、自分の持ち物がその設定の外側に出ていないかを、先に見たほうがよいと思います。
固めを入れること自体は、これからも進めます。ただし、一律に付けるのではなく、埋め込みの実態を調べてから付けます。壊さずに固めを効かせるには、この順番がよいというのが、今回の結論です。