JavaScript
Web
MIDI
WebMIDIAPI


はじめに

Chrome 64からセキュリティー強化のためFeature Policyが有効化される予定です。Web MIDI APIにも影響があるので簡単に解説したいと思います。


Feature Policyとは何か

Cross-origin iframeから利用できるAPIを制限するための仕組みです。ご存知のようにWebから利用できるAPIは日々低レイヤー化が進み、できる事が増えると同時に何か問題があった時のリスクも大きくなってきています。

一方で、ウェブ広告はその内容をページの所有者が検証しないままに、ページの一部としてコンテンツに組み込まれます。もしAPIにセキュリティーホールが見つかった場合、広告配信ネットワークを通じて、多数のウェブサイトに攻撃コードが配布される危険があります。

Feature Policyは広告のようなサードパーティーコンテンツがcross-origin iframeによって組み込まれる際に力を発揮します。

ユーザーの許可を求めるような強力なAPIは、何か問題があった時の影響が大きいAPIと言い換える事もできます。Cross-origin iframe内ではこのような潜在的リスクの高いAPIがデフォルトで禁止されます。具体的にはGeolocation、MIDI、Encrypted Media Extensions、Microphone、CameraなどのAPIが対象です。詳しくはDeprecating Permissions in Cross-Origin Iframesを御覧ください。

Chrome 63までは対象となるWeb MIDI APIのアクセスに対して以下のような警告を表示してきました。JSFiddleなどでWeb MIDI APIを使おうとすると目にするはずです(対応してくれないかなー→中の人は対応したつもりだったけど指定が間違ってた→絶賛修正中)。


(index):46 [Deprecation] requestMIDIAccess usage in cross-origin iframes is deprecated and will be disabled in M64, around January 2018. To continue to use this feature, it must be enabled by the embedding document using Feature Policy, e.g.


Chrome 64以降は、以下のようなエラーメッセージが表示され、requestMIDIAccessは無残にもSecurityErrorでrejectされます。


Midi access has been blocked because of a Feature Policy applied to the current document. See https://goo.gl/EuHzyv for more details.


 


DOMException: Midi has been disabled in this document by Feature Policy.



Feature Policyに対応する

あくまで第三者による意図しないコンテンツからの攻撃を防ぐ事が目的ですから、明示的にAPIの利用を許可する事もできます。一番簡単なのがiframeに対してallowアトリビュートを与えることです。

<iframe src="https://xo.example.com" allow="midi; microphone"></iframe>

このように明示的に許可するAPIを列挙する事で、cross origin iframe内からも許可したAPIが利用できるようになります。

それ以外にも、HTTP Response Headerからコントロールする方法もあるようです。Feature-Policyヘッダを使います。Feature-Policy: midi; microphoneみたいに書きます。:の右側はallowの中身と同じ書式。


少し凝った使い方


落とし穴

Feature Policyのallowで指定した機能は、iframe内で常に利用できるわけではありません。実はデフォルトではsrcに指定されたURLのoriginに対してのみ機能を許可しています。つまり、iframe内で更に別のoriginへ移動したり、srcを指定せずにiframeを作った場合には期待通りの動作をしません。

という事で、もう少し複雑な場合でも適切に指定できるよう、allowのもう少し詳しい書式について説明します。


allowアトリビュートの書式



  • allowアトリビュートは;で区切られた複数のpolicy-directiveを含みます


    • 例1:midi; geolocation; microphone




  • policy-directivefeature-nameの後に0個以上のallow-list-valueをスペース区切りで含みます


    • 例1:midi 今まで通り何も指定しない

    • 例2:midi https://xo.example.com originを1つ指定

    • 例3:midi http://xo.example.com https://xo.example.com originを2つ指定




  • allow-list-valueoriginまたは*'self''src''none'といった特殊なキーワードのどれかです


    • 例1:http://xo.example.com いわゆる今までの例でも見てきたorigin

    • 例2:* ワイルドカード、常に許可




具体例

例えばWeb MIDIに関してFeature Policy導入前と同様にiframe内で一切の制限なく利用したいと思ったら

<iframe allow="midi *" src="//xo.example.com/midi_app.html></iframe>

みたいな書き方をする必要があります。もちろん、これではFeature Policyが導入された意味がないので、適切なoriginに絞って記述するのが望ましいのですが……srcの中身が自分の管理下にある物なら、トップレベルの指定としては許せなくもないです。その代わり、iframe内で内容について責任を持てないコンテンツは一切含めるべきではありませんし、そのようなコンテンツにnavigateするのもNG。


srcを使わない場合への対処

iframeにはsrcを指定せずに、formから得た結果を転送するような記述が可能です。この場合allowに単純にmidiを指定しても、midi 'none'としているのと同じで誰に対しても許可が降りません。このような場合にはpolicy-directiveにoriginを明記します。

<form method="get" action="//xo.example.com/midi.html" target="midi_frame">

<input type="submit" value="run">
</form>
<iframe
name="midi_frame"
allow="midi http://xo.example.com https://xo.example.com">
</iframe>

これによりxo.example.com内にいる限りにおいてはiframe内でWeb MIDIが利用できます。


まとめ

以上、簡単ながらFeature Policyの解説でした。

この機能について初めて相談を受けたのは年Google本社で行われたBlinkOn 7の時だったかと思います。Cross-origin iframeに対してある種のSandboxによって利用できるAPIを制限するという発想は、利用者の負担・利便性への影響を最低限に押さえつつ、攻撃に対して非常に効果のある方法だと感じ、実装が世に出る日を心待ちにしていました。

願わくは、皆さんもこの機能を気に入ってくれますことを。

P.S. 書き終わってからtadfmacさんの記事でも少し触れられている事に気づきました。