Feature PolicyおよびFeature unsized-mediaの導入ガイド
個人的に注目している Feature Policy
というWeb標準の技術と、Feature Policyの中で課せられるルールのうちの1つ、 unsized-media
について。
Feature-Policy
Feature Policyは、ブラウザ内の機能やAPIの使用可否を開発者に提供する機能。AMPはHTMLのサブセットとして強制的にパフォーマンスの劣化を起こす機能にアクセスできないようにするプラットフォームだが、Feature Policyはそれを通常のウェブサイトで、細やかに制御ができるように設計されている。Cntent Security Policy (CSP)と構文は似ているが、CSPはセキュリティをコントロールするのに対して、Feature Policyは機能をコントロールする。開発者側で機能を明示的に制御することで、大規模サービスでの長期にわたる開発運用や、サードパーティ製SDKおよびライブラリの意図しない動作によってサイトのUXが低下するリスクを下げることができる。
多くのポリシーが存在するが、例を挙げると以下のようなユースケースが考えられる。
- サードパーティの動画のデフォルトautoplayを制御する
- フルスクリーン画面の許可をする
- サードパーティスクリプトの同期XHRやdocument.writeを禁止する
- Webフォント使用時のswapを制御
- 不必要に大きい画像のローディング制御
- etc…
また、単に機能を許可/禁止するだけでなく、geolocationなどiframe内では原則として取得できないAPIも、Feature Policyでの許可によってアクセスを可能とすることを意図している。
基本的な構文
Feature Policyは、以下の2つの方法で適用される。
HTTPヘッダ
おそらく基本的にはこちらを使うことになるだろう。Featureには使用したいポリシーを、allow listにはドメインを列挙するか、後述する特殊文字を記述する。
Feature-Policy:<Feature><allow list origin(s)>; <Another feature><another allow list origin(s)>
実際に適用するとこんな感じ。
Feature-Policy: sync-xhr 'self' https://example.com; unsized-media 'none'
これは、同期XHRを配信する同一オリジンまたはexample.comのみで許可することを示す。特殊文字は以下。
iframe allow属性
Iframe内のコンテンツを制御するためには、iframeタグ内に直接ポリシーリストを記述することができる。
<iframe src="https://example.com/iframe/src” allow="fullscreen"></iframe>
Policy一覧
現在のポリシー制御機能のセットは、2つのカテゴリに分類される。
- Enforcing best practices for good user experiences (UXのベストプラクティスを強制)
- Providing granular control over sensitive or powerful features (強力なAPIの柔軟な制御)
Enforcing best practices for good user experiences
sync XHRやdocument.writeなどユーザ体験を損なう振る舞いを3rd partyスクリプトなどのサブリソース含めて(重要)制御する。
policy | 意味 |
---|---|
font-display-late-swap | webフォントのローディング遅延によるフォントの切り替わり |
layout-animations | CSSアニメーションによるlayoutの発生 |
legacy-image-formats | 古い画像フォーマットの配信 |
oversized-images | 実際にレンダリングされるサイズより大きな画像 |
sync-script | 同期的に読み込まれるスクリプト |
sync-xhr | 同期XMLHttpRequest |
unoptimized-images | 最適化されていない画像 |
sync-xhr | 同期XMLHttpRequest |
unsized-media | サイズが未定義のメディアリソース |
Providing granular control over sensitive or powerful features
ユーザの許諾が必要な強力なAPIやデバイスの機能にアクセスするものを制御できる。
policy | 意味 |
---|---|
accelerometer | 加速度センサー |
ambient-light-sensor | 環境光センサー |
autoplay | メディアの自動再生 |
camera | カメラ |
encrypted-media | 暗号化メディア拡張 |
fullscreen | フルスクリーン |
geolocation | 位置情報 |
gyroscope | ジャイロスコープ |
lazyload | 遅延ロード |
microphone | マイク |
midi | Web MIDI |
payment | Web Payment |
picture-in-picture | picture in picture |
speaker | スピーカー |
usb | WebUSB |
vr | VR / XR |
上の表にはExperimentalなものも含まれており、ChromeであればflagやOrigin Trialなどが必要なものもある。詳しくは以下のリンクを参照すること。
プロポーザル段階のものや新しいアイディアなどがissueで議論されておりfeatureの粒度もそれぞれだが、これから更に新しいものが追加される可能性もある。
featureの定義は認識によって異なるのでissue見ると様々な"feature"が確認できますね🤔 https://t.co/kefCHjY2Ez
— Shunya Shishido (@sisidovski) 2019年6月1日
また、ブラウザの対応状況についてはこちらを。基本的にChromeとFirefox, Operaのみ対応しており(各フィーチャーの対応状況はばらばら)、EdgeやSafariは現在対応していない。
Reporting
Feature Policyによる制限は多岐にわたりかつ強力で、プロダクションで強制的に適用するのは難しいケースもある。そのためにReporting APIを使ったポリシー違反を収集するレポーティングのみ行う機能が提供されている。
webappsec-feature-policy/reporting.md at master · w3c/webappsec-feature-policy · GitHub
現在HTTPヘッダを使ったレポーティングの機能はOrigin Trialが必要。
Report-toヘッダ
Report-To: { “group”: “feature-policy”, “max_age”: 86400, “endpoints”: [{“url”: “https://example.com/reports”}] }
上記ヘッダを送信しつつ、Feature Policyヘッダにreport-toを付与する。
Feature-Policy: …; report-to feature-policy
ポリシー違反はPOSTで指定したエンドポイントに送信される。送信されるデータは以下のようになる。
{ "type": "feature-policy-violation", "url": "https://a.featurepolicy.rocks/geolocation.html", "age": 60000, "user_agent": "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", "body": { "featureId": "geolocation", "message": "Geolocation access has been blocked because of a Feature Policy applied to the current document. See https://goo.gl/EuHzyv for more details.", "source_file": "https://a.featurepolicy.rocks/geolocation.html", "line_number": 20, "column_number": 37, "disposition": "enforce" } }
ReportingObserver
ReportingObserver APIを利用することで、JavaScriptからポリシー違反を収集することもできる。
const observer = new ReportingObserver(reportList => { reportList.forEach(report => { console.warn("Whatever you just tried to do was blocked by policy.: " + report.body.featureId); }); }, {"types": ["feature-policy-violation"]}); observer.observe();
unsized-media
unsized-mediaは、画像や動画などのメディアリソースの”本来”のサイズで表示するかを制御するポリシー。画像や動画に明示的にサイズが指定されていない場合、ブラウザはレイアウトを決定できずリソースをロードして初めて描画が行われるが、unsized-mediaはこれにより起きるリフローを防ぐ。サイズの指定がなくポリシー違反を起こしたとき、imgなどのタグは300x150のデフォルトサイズとして扱われるようになる。ポリシーを遵守するためには、width/height属性、CSS、intrinsicSize属性によるサイズ・アスペクト比のいずれか、またはこれらの組み合わせをimgやvideoといったメディア系のタグに付与する必要がある。
intrinsic sizeって?
上述の通りメディアリソースは実際にロードしてみるまでサイズが分からない(レイアウトが決定できない)が、もしリソースそのもののサイズが予め分かっており、それをブラウザに伝える手段があればどうだろうか。 intrinsicsize
属性を指定することで、リソースの実際のサイズを上書きすることができる。
プロポーザルの詳細はこちらで確認できる。
unsized-media
を適用した場合、メディアリソースのサイズは以下のように扱われる。
- width or height の指定がなくintrinsicsizeの指定もない場合、画像は300x150でレンダリングされる
- width or heightが指定されており、intrinsicsizeが指定されていない場合、画像リソース自体のサイズは300x150として扱われ、300x150のアスペクト比を保ちつつ指定されたwidth or heightでレンダリングされる
- width and heightの指定がなく、intrinsicsizeのみ指定されていた場合は、画像はintrinsicsizeをの値をベースにレンダリングされる
DEMO
Layout Stability
unsized-media
を単体で利用してもよいが、同じく現在Origin TrialとなっているLayout Stability APIを併用するとレイアウト崩れの大きさを定量的に収集できるかもしれない。
DEMO
利用するにあたって
2019/06/04現在Chrome Stableで確認するためにはフラグおよびOrigin Trialでトークンを取得する必要がある。
unsized-mediaを利用するにあたっては、 chrome://flags/#enable-experimental-web-platform-features
を、Reporting APIとLayout Stability APIを使うためにはOrigin Trialを。
実際の利用シーン
Feature Policyはポリシーに違反した場合、代替画像で表示されたりそもそもスクリプトが実行できなかったりする。プロダクションでこのような挙動は避けるべきなので、まずはReportingから開始するのがいいだろう。レポートの送り先は集計して観測できれば何でもよい。Sentryなどを利用するか、Elasticsearchに放り込んでKibanaで可視化するといった方法が考えられる。
また、プロジェクトの早い段階で導入しておくことで強い権限を要求するAPIの使用や、意図しないパフォーマンスの劣化を防ぐことができる(または、検知できる)。外部スクリプトが好ましくない挙動をする場合も、提供者側により正確にフィードバックしやすくなる。
Feature Policyは強力かつHTTPヘッダを1つ追加するだけで適用できるので、小さいところからトライしていくとよさそう。
参考URL
- https://github.com/w3c/webappsec-feature-policy
- https://developers.google.com/web/updates/2018/06/feature-policy
- https://developer.mozilla.org/ja/docs/Web/HTTP/Feature_Policy
- https://feature-policy-demos.appspot.com
- https://featurepolicy.info
- https://blog.jxck.io/entries/2018-03-08/feature-policy-permission-delegation.html
ご指摘等あれば@sisidovskiまで。