WebHID API ( 日本語訳 )

Draft Community Group Report 08 January 2021

この文書は、W3Cの文書 "WebHID API , Draft Community Group Report 08 January 2021" の日本語訳です。

WebHID API の正式な文書は英語版のみであり、日本語訳には翻訳に起因する誤りが含まれている場合があります。 英語版の正式な最新の文書は : https://wicg.github.io/webhid/にあります。

この翻訳の元となる文書は現在、コミュニティグループのレポート (Draft Community Group Report)であり、今後も更新されて行きます。

日本語訳GitHub : https://github.com/g200kg/webhid-api-ja/
日本語訳公開URL : https://g200kg.github.io/webhid-api-ja/

また、とりあえず動かしたい場合に有用な手引書が付属文書として用意されています
WebHID API 手引書 : https://g200kg.github.io/webhid-api-ja/EXPLAINER.html

Tatsuya Shinyagaito @ g200kg
誤りその他があれば GitHub 経由などで連絡をお願いいたします。
https://www.g200kg.com/

2021年2月25日

このドキュメントでは、HID(Human Interface Device) プロトコルをサポートするデバイスへのアクセスを提供するための API について説明します。

序章

HID ( Human Interface Device ) とは、人間から入力を受けたり、人間に出力を提供したりするデバイスの一種です。また、HID プロトコルとは、インストール手順を簡略化するために設計されたホストとデバイス間の双方向通信のための標準規格のことを指します。 HID プロトコルはもともと USB デバイス用に開発されましたが、その後 Bluetooth をはじめとする多くのプロトコルで実装されています。

ウェブプラットフォームはすでに多くの HID デバイスからの入力をサポートしています。キーボード、ポインティングデバイス、およびゲームパッドはすべて HID プロトコルを使用して実装されています。しかし、このサポートは、HID 入力を高レベルの入力 API に変換するオペレーティングシステムの HID ドライバに依存しています。ホストの HID ドライバによって十分にサポートされていないデバイスは、ウェブページにアクセスできないことがよくあります。同様に、ホストの HID ドライバでサポートされていないほとんどのデバイスの出力はアクセスできません。

関連文書の 手引書 も参照してください。

目標となるアプリケーション

ニッチデバイス

HID 入力デバイスの最も一般的なクラスは、既存の高レベルの入力 API を介して Web プラットフォームですでに十分にサポートされています。 たとえば、マウス入力には PointerEvent API からアクセスでき、キーボード入力にはキーボード API からアクセスできます。 これらのデバイスからの入力はホストのネイティブ HID ドライバーを使用して処理され、通常、デバイス固有のドライバーや構成が正しく機能する必要はありません。 WebHID は高レベルの入力 API によってすでに十分にサポートされているこのようなデバイスを対象としていません。

HID デバイスの一部のクラスでは、 Web プラットフォームはデバイスの一部の機能をサポートしますが、他の機能へのアクセスを制限します。 たとえば、ゲームパッド API は、ほとんどのゲームコントローラーの入力機能をサポートしていますが、 LED インジケーターやオーディオなどのあまり一般的ではない機能はサポートしていません。 これらの機能は、ホスト API によって十分にサポートされていないことが多く、ユーザーエージェント内にサポートを追加すると、非常に複雑になる可能性があります。 WebHID は高レベル API によって提供される機能が不完全な場合に、アプリケーションに代替手段を提供します。

HIDデバイスの多くは Web プラットフォーム API ではサポートされていません。 HID 仕様では、仮想現実の制御、フライトシミュレーター、医療機器など、HID を介してサポートできるさまざまなデバイスについて説明しています。 WebHID を使用すると、追加のドライバーやユーザーエージェントの変更を必要とせずに、これらのデバイスを使用できます。

試作、趣味、教育向けの機器

HIDは、デバイスが使用されるホストごとにドライバーを必要とせず、ホストの汎用 HID ドライバーを使用できるようにするため、試作や趣味のためのアプリケーションにとって魅力的です。 また簡素化されたインストール手順によって、教師は各ホストのシステム構成を変更することなく、デバイスを生徒の教室に簡単に展開できます。 Web プラットフォームを介して HID デバイスへのアクセスを提供すると、特に現在ホスト固有のアプリケーションでのみサポートされているデバイスの場合、インストールに必要な要件がさらに軽減されます。

セキュリティとプライバシーに対する考慮

デバイスへのアクセスの悪用

HID 周辺機器は、ユーザーの明示的な同意なしに、ページにアクセスできるようにすべきではない強力な機能を公開してしまう場合があります。 たとえば、HID 周辺機器には、周囲の情報を収集できるセンサーが搭載されているかも知れません。 あるデバイスには、開示または上書きしてはならない個人情報が保存されている場合があります。 多くのデバイスは、デバイスのファームウェアをアップグレードできる機能を公開しています。 オペレーティングシステムは通常、アプリケーションからHIDデバイスへのアクセスを制限しません。このアクセスが悪用されて、デバイスが損傷したり、デバイスに保存されているデータが破損したりする場合があります。

場合によっては、デバイスが Web ページからアクセスしてはならない機能を公開することがあります。 特定のデバイスは、ベンダーと製品の ID によってデバイスを認識し、列挙中にそのようなデバイスを非表示にすることでブロックされる場合があります。 HID レポート記述子を使用すると、デバイスは独自の機能を記述できます。この情報を使用して、ベンダーと製品のIDを事前に知ることができない場合でも、デバイスのクラスをブロックできます。 これは、レポート記述子内のコレクションに割り当てられた使用量の値を調べることで実現できます。 たとえば、キーボードのようなデバイスへのアクセスは、キーボード使用IDを持つトップレベルのコレクションを含むデバイスへのアクセスを拒否することで拒否できます。

不正使用を軽減するために、ユーザーが明示的なアクセスを許可するまで、ページはデバイスを使用できません。 アクセスは最初にページによって要求される必要があり、使用可能なすべてのデバイスを表示する選択肢からユーザーがデバイスを選択すると許可されます。 ページからのアクセスが許可されると、デバイスが使用中であることを示すインジケーターが表示されます。

デバイスへの攻撃

HID プロトコルは非常に用途が広く、この汎用性を利用するさまざまなデバイスが設計されています。 その結果、デバイスへのアクセスによって悪意のあるページがデバイス自体に損害を与える可能性があります。 HID ペリフェラルでは、カスタム HID レポートを使用してファームウェアアップデートを送信できるようにするのが比較的一般的です。 デバイスメーカーは、ファームウェアのバイナリが本物であることを確認するためにファームウェアアップグレードメカニズムを設計するように注意する必要があります。また、デバイスのセキュリティを低下させる可能性のあるダウングレードから保護する必要があります。 信頼できないアップグレードを使用して、デバイスに新しい機能を追加したり、まったく異なるタイプのデバイスになりすましたりする可能性があります。

予想範囲外の入力が行われると、一部のデバイスが損傷する可能性があります。 たとえば、ページが正常でない振動コマンドを送信すると、ゲームパッドの振動機能が破損する可能性があります。

一般に、これらのタイプの攻撃はデバイス固有であり、 APIレベルで軽減することはできません。

ホストへの攻撃

悪意のあるページがデバイスにアクセスした場合、場合によっては、このアクセスを使用してホストを攻撃する可能性があります。 主な懸念事項は、デバイスを使用して信頼される入力イベントを生成できるかどうかです。 これらのイベントは、ユーザーの意図のプロキシとして機能し、より強力な Web プラットフォーム機能にアクセスするために使用できます。

マウスとキーボードは通常、HID 周辺機器として実装されており、信頼できる入力を生成することもできます。 HID キーボードまたはマウスには、プログラム可能なマクロなどの高度な機能が含まれている場合があります。これらの機能は、入力のシーケンスを格納し、後でシーケンスを再生できるようにします。 デバイスメーカーは、悪意のあるアプリが予期しない入力シーケンスでデバイスを再プログラミングしないようにそのような機能を設計するように注意する必要があります。また、ユーザーの明示的な同意なしにマクロ再生が起動されないように保護する必要があります。

軽減策

セキュリティとプライバシーのリスクを軽減するために、ユーザーエージェントはデバイスへのアクセスを要求するための選択肢ベースのフローを実装することをお勧めします。 ページが HID デバイスへのアクセスを要求すると、ユーザーエージェントは、ページがデバイスにアクセスすることをユーザーに通知するダイアログを表示する必要があります。 ダイアログには、接続されている HID デバイスのリストが表示され、ページに表示されるデバイスを選択するようにユーザーに求められます。 偶発的なクリックの可能性を減らすために、ダイアログには 2 つの個別のインタラクションが必要です。 1 つはリストからデバイスを選択するためのインタラクションで、もう 1 つは選択を確認してダイアログを閉じるためのインタラクションです。

デバイスの列挙

        dictionary HIDDeviceFilter {
            unsigned long vendorId;
            unsigned short productId;
            unsigned short usagePage;
            unsigned short usage;
        };

        dictionary HIDDeviceRequestOptions {
            required sequence<HIDDeviceFilter> filters;
        };

        [
            Exposed=Window,
            SecureContext
        ]
        interface HID : EventTarget {
            attribute EventHandler onconnect;
            attribute EventHandler ondisconnect;
            Promise<sequence<HIDDevice>> getDevices();
            Promise<sequence<HIDDevice>> requestDevice(
                HIDDeviceRequestOptions options);
        };

        [SecureContext] partial interface Navigator {
            [SameObject] readonly attribute HID hid;
        };
      

次の手順で `match` が返される場合、 {{HIDDevice}} |device| は {{HIDDeviceFilter}} |filter| に 一致するデバイス となります:

  1. |filter|.{{HIDDeviceFilter/vendorId}} が存在し、|device|.{{HIDDevice/vendorId}} が |filter|.{{HIDDevice/vendorId}} と等しくない場合、`mismatch` を返します。
  2. |filter|.{{HIDDeviceFilter/productId}} が存在し、|device|.{{HIDDevice/productId}} が |filter|.{{HIDDevice/productId}} と等しくない場合、`mismatch` を返します。
  3. |filter|.{{HIDDeviceFilter/usagePage}} が存在する場合は、|device|.{{HIDDevice/collections}} の {{HIDCollectionInfo}} コレクションに対して繰り返し処理します。 [=一致するコレクション=] がない場合は、`mismatch` を返します。
  4. `match` を返します。

次の手順で match が返される場合、{{HIDCollectionInfo}} |collection| は {{HIDDeviceFilter}} |filter| に 一致するコレクション となります:

  1. |filter|.{{HIDDeviceFilter/usagePage}} が存在し、|device|.{{HIDCollectionInfo/usagePage}} が |filter|.{{HIDDeviceFilter/usagePage}} と等しくない場合、mismatch を返します。
  2. |filter|.{{HIDDeviceFilter/usage}} が存在し、|device|.{{HIDCollectionInfo/usage}} が |filter|.{{HIDDeviceFilter/usage}} と等しくない場合、mismatch を返します。
  3. match を返します。

次の手順で有効が返された場合、{{HIDDeviceFilter}} |filter| は 有効 です:

  1. |filter|.{{HIDDeviceFilter/productId}} が存在し、|filter|.{{HIDDeviceFilter/vendorId}} が存在しない場合は、 無効 を返します。
  2. |filter|.{{HIDDeviceFilter/usage}} が存在し、|filter|.{{HIDDeviceFilter/usagePage}} が存在しない場合は、無効を返します。
  3. 有効 を返します。

UA は、システムに接続されているすべてのデバイスを列挙 できる必要があります (MUST)。 アルゴリズムが列挙を要求するたびにこの作業を実行する必要はありません。 UA は、最初の列挙を実行した結果をキャッシュしてから、デバイスの接続および切断イベントの監視を開始し、接続されたデバイスをキャッシュされた列挙に追加し、切断されたデバイスを削除しても構いません (MAY)。 この方法は、オペレーティングシステムコールの数を減らすために推奨されます。

イベント

          dictionary HIDConnectionEventInit : EventInit {
              required HIDDevice device;
          };

          [
              Exposed=Window,
              SecureContext
          ] interface HIDConnectionEvent : Event {
              constructor(DOMString type, HIDConnectionEventInit eventInitDict);
              [SameObject] readonly attribute HIDDevice device;
          };

          dictionary HIDInputReportEventInit : EventInit {
              required HIDDevice device;
              required octet reportId;
              required DataView data;
          };

          [
              Exposed=Window,
              SecureContext
          ] interface HIDInputReportEvent : Event {
              constructor(DOMString type, HIDInputReportEventInit eventInitDict);
              [SameObject] readonly attribute HIDDevice device;
              readonly attribute octet reportId;
              readonly attribute DataView data;
          };
        

HID コレクションとレポート情報

          enum HIDUnitSystem {
              "none", "si-linear", "si-rotation", "english-linear",
              "english-rotation", "vendor-defined", "reserved"
          };

          dictionary HIDReportItem {
              boolean isAbsolute;
              boolean isArray;
              boolean isBufferedBytes;
              boolean isConstant;
              boolean isLinear;
              boolean isRange;
              boolean isVolatile;
              boolean hasNull;
              boolean hasPreferredState;
              boolean wrap;
              sequence<unsigned long> usages;
              unsigned long usageMinimum;
              unsigned long usageMaximum;
              unsigned short reportSize;
              unsigned short reportCount;
              byte unitExponent;
              HIDUnitSystem unitSystem;
              byte unitFactorLengthExponent;
              byte unitFactorMassExponent;
              byte unitFactorTimeExponent;
              byte unitFactorTemperatureExponent;
              byte unitFactorCurrentExponent;
              byte unitFactorLuminousIntensityExponent;
              long logicalMinimum;
              long logicalMaximum;
              long physicalMinimum;
              long physicalMaximum;
              sequence<DOMString> strings;
          };

          dictionary HIDReportInfo {
              octet reportId;
              sequence<HIDReportItem> items;
          };

          dictionary HIDCollectionInfo {
              unsigned short usagePage;
              unsigned short usage;
              octet type;
              sequence<HIDCollectionInfo> children;
              sequence<HIDReportInfo> inputReports;
              sequence<HIDReportInfo> outputReports;
              sequence<HIDReportInfo> featureReports;
          };
        

デバイスの使用法

        [
            Exposed=Window,
            SecureContext
        ] interface HIDDevice : EventTarget {
            attribute EventHandler oninputreport;
            readonly attribute boolean opened;
            readonly attribute unsigned short vendorId;
            readonly attribute unsigned short productId;
            readonly attribute DOMString productName;
            readonly attribute FrozenArray<HIDCollectionInfo> collections;
            Promise<undefined> open();
            Promise<undefined> close();
            Promise<undefined> sendReport([EnforceRange] octet reportId, BufferSource data);
            Promise<undefined> sendFeatureReport([EnforceRange] octet reportId, BufferSource data);
            Promise<DataView> receiveFeatureReport([EnforceRange] octet reportId);
        };
      

統合

権限ポリシー

この仕様は、{{Navigator/hid}} 属性を {{Navigator}} オブジェクト上に公開するかどうかを制御する機能を定義します。

この機能の機能名は `"hid"` です。

この機能のデフォルトの許可リスト ([=default allowlist=]) は `["self"]` です。

非規範として標記されたセクションと同様に、この仕様のすべてのオーサリングガイドライン、図、例、および注記は非規範的です。 この仕様の他のすべては規範的です。

このドキュメントのキーワード MAY および MUST は、ここに示すように、すべて大文字で表示される場合にのみ、BCP 14 [RFC2119] [RFC8174]で説明されているように解釈されます。