銀色うつ時間

思い出すたび何か胸につっかえてるだけ

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が低下するリスクを下げることができる。

github.com

blog.jxck.io

多くのポリシーが存在するが、例を挙げると以下のようなユースケースが考えられる。

  • サードパーティの動画のデフォルト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のみで許可することを示す。特殊文字は以下。

  • * 全てのドメインで許可される。
  • 'none’ 全てのドメインで禁止される。
  • ‘self’ 同一オリジン上は許可し、それ以外のオリジンでは制限する。

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 同期XMLHttp​Request
unoptimized-images 最適化されていない画像
sync-xhr 同期XMLHttp​Request
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などが必要なものもある。詳しくは以下のリンクを参照すること。

github.com

プロポーザル段階のものや新しいアイディアなどがissueで議論されておりfeatureの粒度もそれぞれだが、これから更に新しいものが追加される可能性もある。

また、ブラウザの対応状況についてはこちらを。基本的にChromeFirefox, Operaのみ対応しており(各フィーチャーの対応状況はばらばら)、EdgeやSafariは現在対応していない。

developer.mozilla.org

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属性、CSSintrinsicSize属性によるサイズ・アスペクト比のいずれか、またはこれらの組み合わせをimgやvideoといったメディア系のタグに付与する必要がある。

intrinsic sizeって?

上述の通りメディアリソースは実際にロードしてみるまでサイズが分からない(レイアウトが決定できない)が、もしリソースそのもののサイズが予め分かっており、それをブラウザに伝える手段があればどうだろうか。 intrinsicsize 属性を指定することで、リソースの実際のサイズを上書きすることができる。

プロポーザルの詳細はこちらで確認できる。

github.com

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

ご指摘等あれば@sisidovskiまで。