WebSocket のセキュリティ
最終更新日 2024年05月07日(火)
Table of Contents
WebSocket プロトコルはまだ成熟していないテクノロジーのため、いくつかのリスクがあります。何十年もの経験を経たおかげで、Web コミュニティは HTTP セキュリティに関するベストプラクティスをいくつか得ることができました。しかし WebSocket 環境におけるセキュリティベストプラクティスは十分に確立されておらず、発展途上にあります。それでも、いくつかのテーマが浮かび上がっているので、この記事で説明します。
WSS
皆さんは当然ながら安全でない ws://
トランスポートよりも安全な wss://
プロトコルのほうを選ぶことでしょう。HTTPS のように、WSS (WebSocket over SSL/TLS) は暗号化されているので、Man-In-The-Middle (マン イン ザ ミドル) 攻撃から守られます。WebSocket に対する様々な攻撃は、トランスポートが安全であれば不可能になりました。
トンネリングを使用しない
比較的簡単なのは、任意 TCP サービスを WebSocket を使ってトンネリングすることです。そうすれば、たとえばデータベース接続を直接ブラウザまでトンネリングできます。しかし、これは非常に危険です。このようにすると、クロスサイトスクリプティング攻撃の場合にブラウザ内の攻撃者がこれらのサービスにアクセス可能になり、リモートブランチ全体へのXSS 攻撃のエスカレーションが可能になってしまいます。
トンネリングは可能な限り使用しないことをお勧めします。代わりに、WebSocket より安全でチェックされたプロトコルを開発しましょう。
クライアント入力を検証
WebSocket 接続はブラウザの外側で容易に確立できるので、恣意的データに対処する必要があると考えましょう。クライアントから送られるデータと同じように、入力データは処理する前に慎重に検証してください。SQL インジェクション攻撃は、WebSocket 上では、HTTP 上と同じように可能です。
サーバーデータを検証
サーバーから返ってきたデータにも、同等の疑いをかけるべきです。クライアント側で受け取ったメッセージも必ずデータとして処理しましょう。直接 DOM に割り当てようとしたり、コードとして評価しようとしてはなりません。応答が JSON の場合は、必ず JSON.parse()
を使用してデータを安全に解析してください。
認証/承認
WebSocket プロトコルでは、承認や認証が処理されません。事実上、これは認証を経た後のページから開いた WebSocket にはいかなる部分の認証も「自動的」に届かないことになるため、WebSocket 接続も保護する措置をとる必要があります。
これは、WebSocket が認証でよく使われる標準の HTTP ヘッダーを通過するので、様々な方法で行えます。これは、Web ビューで使用しているのと同じ認証メカニズムを WebSocket 接続でも使用できることを意味します。
WebSocket ヘッダーを JavaScript からカスタマイズできないので、ブラウザから送信された「暗黙の」認証 (つまり Basic や cookie) に限られてしまいます。さらに、よくあるのは WebSocket を扱うサーバーが、「通常の」 HTTP リクエストを扱うサーバーと完全に分離されることです。この結果、承認ヘッダーの共有が難しくなったり不可能になったりします。
そこで、WebSocket 認証問題を十分に解決すると思われる 1 つのパターンが「チケット」ベースの認証システムです。大まかにいえば、これは次のような仕組みです。
クライアント側のコードが WebSocket を開くよう決定すると、承認「チケット」を得るため HTTP サーバーに接続します。
HTTP サーバーはこのチケットを作成します。チケットに一般に含まれるのは、何らかのユーザー/アカウント ID、チケットを要求しているクライアントの IP、タイムスタンプ、そして必要となる他のあらゆる内部記録管理です。
サーバーはこのチケットを (データベースあるいはキャッシュ内に) 保管して、クライアントにも返します。
クライアントは WebSocket 接続を開き、この「チケット」を初期ハンドシェイクの一環として送ります。
するとサーバーはこのチケットを比較し、ソース IP を調べて、チケットが再使用されておらず失効していないことを確認して、他のあらゆる権限チェックを行います。すべてがうまくいくと、今度は WebSocket 接続が検証されます。
Armin Ronacher が初めてこのパターンを私たちに教えてくださったことに感謝します。
当初のヘッダー
WebSocket 標準で定義されている Origin
ヘッダーフィールドは、Web ブラウザが WebSocket リクエストを送り出す URL に設定します。これを使用して様々なホストから送られる WebSocket 接続を見分けたり、ブラウザから行われた接続と他の種類のネットワーククライアントから行われた接続を見分けたりできます。ただし、Origin
ヘッダーは注意が必要であることを忘れないでください。ブラウザを使用しないクライアントは簡単に Origin
ヘッダーを任意の値に設定して、ブラウザの「ふり」をしてしまいます。
Origin
ヘッダーは、AJAX リクエストが使用する X-Requested-With
ヘッダーと似ていると思われるでしょう。Web ブラウザが送信する X-Requested-With: XMLHttpRequest
のヘッダーは、ブラウザによって作成された AJAX リクエストと直接作成されたリクエストを区別するのに使用できます。しかしこのヘッダーはブラウザ以外のクライアントが簡単に設定できてしまうため、認証ソースとして信頼できません。
同じように、注意が必要なメカニズムとして使用できる Origin
ヘッダーは様々な場所やホストから送られた WebSocket リクエストを見分けるのに役立ちます。しかし、これを認証ソースとして信頼してはなりません。