本稿で扱う内容

HTML / CSS / JavaScriptの3点セットを用い、ブラウザでwebサイトを表示させる際に最低限考慮しておきたいセキュリティを、特にフロントエンドから堅牢化できるものに特化してざっくりと振り返ります。
今回はフロントエンド開発環境のセキュリティについてはスコープ外とし(例: 有名「npm」パッケージに仮想通貨を盗むコードが混入 ~「Visual Studio Code」拡張機能にも)、もっぱら本番環境をメインに考えるものとします。

フロントエンドのセキュリティの必要性

webセキュリティはその多くがフロントエンドというよりはバックエンド(サーバーサイド)またはインフラストラクチャに深く関係します。ただし、フロントエンドエンジニアはセキュリティを考えなくていいわけではありません。静的なHTMLサイトであってもクロスサイトスクリプティング(XSS)など攻撃を仕掛けられる潜在的な脆弱性があります。こうした攻撃の概要を理解しHTML(とJavaScript)の記述を工夫することでフロントエンドを堅牢化することができます。

参考: これからのフロントエンドセキュリティ – SlideShare

XSS

XSSとはwebページ上で意図せず任意のスクリプトが実行されてしまう脆弱性です。ユーザーが入力したデータの適切なエスケープ処理を忘れたりすることが脆弱性となり得ます。

XSSの種類

  • reflected型
  • stored型
  • DOM型

参考: XSSに関して今一度学ぼう – Qiita

XSS を防ぐには

サニタイジング(エスケープ)

XSSはHTMLの特定の文字(< > ' " &)を利用する攻撃なので、これらをHTMLエンティティ文字(&lt;&gt;&amp;など)に置換することで防げる。この処理をサニタイジングまたはエスケープと呼ぶ。
参考: XSS対策:どの文字をエスケープするべきなのか – ockeghem’s blog

オリジンの確認

フロントエンドのセキュリティの中でも重要な概念がオリジン(origin)の確認です。

ウェブコンテンツのオリジンOriginは、ウェブコンテンツにアクセスするために使われるURLのスキーム (プロトコル)、 ホスト (ドメイン)、 ポート…によって定義されます。スキーム、ホスト、ポートがすべて一致した場合のみ、二つのオブジェクトは同じオリジンであると言えます。
操作によっては同じオリジンのコンテンツに限定されており、この制約は CORS を使用して緩和することができます。
Origin (オリジン) – MDN Web Docs 用語集より

  • スキーム
  • ホスト
  • ポート

の3つの組み合わせがオリジンです。

オリジン間保護の仕組み

下記の5つが主要なオリジン保護の仕組みです。

  • CSP (Content Security Policy)
  • CORS (Cross Origin Resource Sharring)
  • CORP (Cross Origin Resource Policy)
  • CORB (Cross Origin Resource Read Blocking)
  • SameSite Cookie

参考: これからのフロントエンドセキュリティ – SlideShare

CORS

CORS (Cross Origin Resource Sharring)は、他のオリジンからリソースを取得して良いかの許可のこと。
CORSを指定することで、CDNなど、クロスオリジンなリソースをセキュアに利用できます。

crossorigin 属性

audio, img, link, script, videoの各要素(つまり音声、画像、動画、JavaScript、CSS、webフォントなど)に使用できる属性。CORS への対応を提供し、要素が読み取るデータのために CORS リクエストの構成を有効にします。

参考: HTML crossorigin 属性 – MDN Web Docs

サブリソース完全性(integrity属性)

サブリソース完全性 (SRI) はCDN などから取得したリソースが意図せず改ざんされていないかをブラウザーが検証するセキュリティ機能です。
script要素やlink要素のintegrity属性に指定することによって使用します。

<a href="https://example.com/example-framework.js">https://example.com/example-framework.js</a>

参考: サブリソース完全性 – MDN Web Docs

モダンブラウザのSameSite Cookie仕様変更

現行モダンブラウザ(Google Chrome 80〜、Firefox 79〜)においては、SameSiteが設定されていないCookieはデフォルトでSameSite=Laxとして扱われるようになり、SameSiteを設定していないCookieの3rd party利用が制限されることとなりました。従来と同じ挙動とするにはSameSite属性をNoneに指定し、さらにSecure属性の指定が必要です。
参考: Changes to SameSite Cookie Behavior – A Call to Action for Web Developers – Mozilla Hacks

Tabnabbing対策

a要素とtarget="_blank"属性を使って他サイトのページにリンクを貼ると、自サイトがパフォーマンスやセキュリティのリスクにさらされる可能性があります。
これは「別のウィンドウから開かれたウィンドウは、主ウィンドウへの参照を保持する」というwindow.openerプロパティの仕様のためです。つまり遷移先のページにwindow.opener.location = newURLというコードを書いておくと、遷移先のページに移動した瞬間に主ページ(遷移前のページ)のURLが自動的にnewURLへリダイレクトされてしまいます。
この手法はTabnabbingと呼ばれフィッシング詐欺に利用されることもあります。
参考: リンクのへの rel=noopener 付与による Tabnabbing 対策
これを防ぐ簡単な方法として、下記のようにtarget="_blank"属性のあるa要素に対してrel="noopener"属性を合わせて書いておくと良い。

<a href="https://example.com" target="_blank" rel="noopener">
  Example Site
</a>

なおChrome 88から target="_blank" 属性のあるa要素は、デフォルトでrel="noopener"と同じ挙動になりました。しかし、2022年1月現在はまだrel="noopener"属性はレガシーブラウザ対応として引き続き記述しておくと良いようです。
参考: Links to cross-origin destinations are unsafe – web.dev
とはいえ2022年6月に予定されるIE11サポート終了でこのあたりも不要になるかもしれませんね。

フロントエンドセキュリティのチェック方法

webhint

Microsoft製の品質チェック/監査ツールwebhintはフロントエンドのセキュリティを簡単にチェックできるツールです。
このツールはGoogleのLighthouseと同じようにChrome Dev Toolsを利用する監査ツールで、Lighthouseを利用したことがある人なら簡単に使いこなせると思います。webhintはアクセシビリティやパフォーマンスやベストプラクティスやPWA対応といった監査項目こそLighthouseと共通していますが、Pitfall(HTMLのエラーチェック)とSecurityというwebhint独自の監査項目があります。これが本当に強力で、Securityの指摘を参考にすればCORSに関する脆弱性などの確認・修正が簡単にできます。

Observatory by Mozilla

Observatory by MozillaにURLを入れスキャンさせるとレスポンスヘッダーのセキュリティ監査ができます。レスポンスヘッダーはサーバーサイドのweb技術ですが、コンテンツセキュリティポリシー (CSP)などmetaタグを使ってフロント側からも制御できる項目もあります。個々のセキュリティ項目をフロント/バックエンドのどちら側から制御するのか決めておくと良いでしょう。

所感

webセキュリティはその多くがサーバサイドとインフラストラクチャに深く関わるものではありますが、フロントエンドエンジニアも最低限のセキュリティ知識を持っておく必要があるでしょう。
静的なHTMLサイトであっても必ず存在するセキュリティリスクをより最小化し、堅牢化すること自体が大事なのはもちろん、基本を押さえておくことで、3rd Party Cookieの仕様変更など突然の大きな変化があった際にもスムースに対応ができるようになるのではないかと思います。webhintなどの便利なツールを利用して、セキュリティ観点での品質管理を効率的に業務に取り込んでいきたいですね。