trifle

技術メモ

Mozilla Observatory を試した

observatory.mozilla.org

Netlify でホスティングしている自分のサイト https://hellorusk.net をよりセキュアにしてみようと考えた. もちろん, 静的サイトなのでプライバシーに関わる情報を持っているわけではないし, GitHubソースコードを上げてしまってすらいるのだけど, あくまで勉強用ということで...

f:id:HelloRusk:20190820172443p:plain:w400

初期評価は D だった.

f:id:HelloRusk:20190820172604p:plain

減点法の採点がなされている.

既にクリアしていた項目

Cross-origin Resource Sharing

Access-Control-Allow-Origin というHTTPヘッダがあり, CDN で JS/CSS を提供したり, API を提供したりする場合には付いている.

$ curl -i "https://fonts.googleapis.com/css?family=Noto+Sans+JP:300"
HTTP/2 200
content-type: text/css; charset=utf-8
access-control-allow-origin: *
(略)

逆に言えば, そのような特殊なケース以外は Cross-origin Resource Sharing(オリジン間ソース共有)をするべきではないので, Access-Control-Allow-Origin は付けないでよい.

http-strict-transport-security (HSTS), Redirection

HTTP でアクセスしても HTTPS にリダイレクトさせていること.
hellorusk.net の場合は Netlifyが自動でやってくれている.

改善した項目

X-Frame-Options

クリックジャッキングという攻撃手法がある. 普通のサイトをクリックしているつもりが, 攻撃者のサイトをクリックさせることになってしまう.

ja.wikipedia.org

これを防止するために X-Frame-Options: DENY を付ける.

X-Content-Type-Options

ブラウザによっては(特にIE), ファイルの種別を判定するために, 悪意のあるファイルであっても検査して読み込んでしまう(sniffing)ようなことがあるようで, これを防止するために(すなわち書かれている Content-Type に従ってファイルの種別を判定するために)X-Content-Type-Options: nosniff としておく.

X-XSS-Protection

XSS をブラウザが検知した時に, ブラウザにページのローディングをやめさせるような機能.
X-XSS-Protection: 1; mode=block で有効化する.
この X-XSS-Protection は, 後述する Content Security Policy がしっかり設定されていてインラインの script 等を防止できていれば, いらない機能ではあるのだが, 実際 Content Security Policy を厳密に設定するのは難しい場合もあったりするので, これを設定するのは第一段階としては有効なようだ.

改善できなかった項目

Content Security Policy

Content Security Policy は, サイトのリソースはどこから読み込まれるべきか, を指示するもの. うまく設定できればインラインの JS の読み込みを阻止できるので, XSS に対する最大級の予防策となる.
Google の記事がとても分かりやすい.

developers.google.com

説明は上に譲るとして, 私の場合はどうだったかを書いておく.
まず, 上の記事の最後にある「ユースケース 3: SSL のみ」を実践してみた.

ユースケース 3: SSL のみ
結婚指輪のディスカッション フォーラムの管理者が、すべてのリソースを安全なチャンネルからのみ読み込めるようにしたいと考えていますが、あまり多くのコードを記述したくありません。インライン スクリプトとインライン スタイルが多数使われているサードパーティ フォーラム ソフトウェアの大量のコードを書き直すことは、管理者の能力を超えています。 効果的なポリシーは次のとおりです。
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
default-src で https: が指定されていますが、スクリプトとスタイルのディレクティブは、その参照元を自動的に継承することはありません。 各ディレクティブは、特定のタイプのリソースでデフォルト値を完全に上書きします。

本当は unsafe-inline は付けるべきではない. Mozilla Observatory の減点対象だ. とはいえ, 私の場合, インラインのスクリプトは無いと困るのである.
実際, unsafe-inline を外して Content-Security-Policy-Report-Only を使ってみた. これはポリシーを実際に適用することなく違反を調べることができるので, ポリシーが厳しすぎることによってサイトが down してしまう危険が無く, ポリシーを試すのによい. すると,

f:id:HelloRusk:20190820181931p:plain

怒られが多数発生してしまった. hellorusk.net は Next.js という JS フレームワークで構築しているので当然ではある.

確かに, やむをえずインラインスクリプトを使う場合の方法もある.

qiita.com

一つはスクリプト自体のハッシュを生成して, それをヘッダに含めるという方法である(上の画像の a hash (...) is required. の部分だ). ただ自分の場合, JS が webpack によってめちゃくちゃ Code Splitting されてしまっていて, 数が膨大になってしまっている. 一体何個のハッシュをヘッダに含めることになるのやら. それに, 中のコードをちょっと書き直しただけでそのハッシュが全部変わってしまうのも怖い. よって却下.
もう一つは, インラインスクリプトnonce-[ハッシュ値] という属性を付けるという方法である. webpack にもそのための設定項目がある(Content Security Policies). が, ハッシュ値は予測できないものでなければならず(そうでなければ unsafe-inline を付けているのと変わらない), 静的サイトにおいてはハッシュ値を動的に変更していくことなど不可能なので, 却下.

以上より, Content Security Policy の項目をクリアすることは諦めた.

Subresource Integrity

Subresource Integrity(サブリソース完全性)は, CDN から取得したリソースが改ざんされていないかをブラウザがチェックする機能である.

developer.mozilla.org

script タグなどに integrity 属性で指定したハッシュ値を入れることでチェックできる.
これは行けそうだったのだが, 思いがけない問題に直面してしまった. 私は自分のサイトに Google Analytics を入れていて, そのために gtag.js というのを CDN から読み込ませているのだが, 配信側が CORS を https ではなく http で設定していたのである.

$ curl -i -s https://www.googletagmanager.com/gtag/js | head -n 3
HTTP/2 200
content-type: application/javascript; charset=UTF-8
access-control-allow-origin: http://www.googletagmanager.com

この通り, http である.
これはよくない. gtag.js を https のリンクで読み込ませてしまうと, この CORS のポリシーで弾かれてしまうし, かといって http で読み込ませてしまうと https の中に http が混在していることになり, 別の怒られが発生してしまう.

f:id:HelloRusk:20190820185243p:plain
Netlify での怒られ

f:id:HelloRusk:20190820185418p:plain
Mozilla での怒られ

以上より, Subresource Integrity の項目をクリアすることは諦めた.




中途半端な部分も多かったけれど, 最終的には B まで評価が上がった.

f:id:HelloRusk:20190820185541p:plain:w400

f:id:HelloRusk:20190820185654p:plain

冒頭でも述べたように, 趣味の静的サイトでこのようなことを行う意味は全く無いのだけど, 色々勉強になったのでよかった.