WebsocketsクライアントAPIのHTTPヘッダー


201

カスタムHTTPヘッダーを、これをサポートするHTTPヘッダークラ​​イアントを使用してWebsocketクライアントに追加するのは簡単なようですが、JSON APIでそれを行う方法を見つけることができません。

しかし、仕様ではこれらのヘッダーをサポートする必要があるようです。

誰でもそれを達成する方法についての手掛かりがありますか?

var ws = new WebSocket("ws://example.com/service");

具体的には、HTTP Authorizationヘッダーを送信できる必要があります。


14
良い解決策は、WebSocketを許可なしで接続できるようにすることですが、サーバーをブロックしてWebSocketからの許可を受け取り、onopenイベントで許可情報を送信することです。
モート

@Motesの提案が最も適しているようです。onOpenから認証呼び出しを行うのは非常に簡単で、認証応答に基づいてソケットを受け入れたり拒否したりできます。私は最初にSec-WebSocket-Protocolヘッダーで認証トークンを送信しようとしましたが、それはハックのように感じられます。
BatteryAcid 2017年

@モートこんにちは、「サーバーでブロックして待機する」部分について説明していただけますか?「auth」メッセージが出るまでメッセージを処理しないなどの意味ですか?
ヒマル

@Himal、はい、サーバー設計では、接続の開始時にデータを送信したり、承認以外のデータを受け入れたりしてはなりません。
モート

@Motes返信ありがとうございます。最初のconnectリクエストをブロックできないことを理解しているため、ブロッキング部分に少し戸惑いました。私はバックエンドでDjangoチャネルを使用しており、connectイベント時に接続を受け入れるように設計しました。次に、receiveイベントに「is_auth」フラグを設定します(有効な認証メッセージが表示された場合)。is_authフラグが設定されておらず、認証メッセージでない場合は、接続を閉じます。
ヒマル

回答:


211

2回更新

短い答え:いいえ、指定できるのはパスとプロトコルのフィールドのみです。

より長い答え:

JavaScript WebSockets APIには、クライアント/ブラウザーが送信する追加のヘッダーを指定するメソッドはありません。HTTPパス( "GET / xyz")とプロトコルヘッダー( "Sec-WebSocket-Protocol")は、WebSocketコンストラクターで指定できます。

Sec-WebSocket-Protocolヘッダー(WebSocket固有の認証で使用されるように拡張されることもある)は、WebSocketコンストラクターのオプションの2番目の引数から生成されます。

var ws = new WebSocket("ws://example.com/path", "protocol");
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]);

上記の結果は、次のヘッダーになります。

Sec-WebSocket-Protocol: protocol

そして

Sec-WebSocket-Protocol: protocol1, protocol2

WebSocket認証/承認を実現するための一般的なパターンは、WebSocketクライアントをホストしているページがサーバーにチケットをリクエストし、WebSocket接続のセットアップ中にこのチケットをプロトコルフィールドのURL /クエリ文字列で渡すチケットシステムを実装することです。または、接続が確立された後の最初のメッセージとして必要です。その後、サーバーは、チケットが有効である(存在する、まだ使用されていない、チケットのIPでエンコードされたクライアントIP、チケットのタイムスタンプが新しいなど)場合にのみ、接続の継続を許可します。WebSocketセキュリティ情報の概要は次のとおりです。https//devcenter.heroku.com/articles/websocket-security

以前は基本認証がオプションでしたが、これは廃止されており、最新のブラウザーはヘッダーが指定されていても送信しません。

基本認証情報(非推奨)

Authorizationヘッダーは、WebSocket URIのユーザー名とパスワード(またはユーザー名のみ)フィールドから生成されます。

var ws = new WebSocket("ws://username:password@example.com")

上記の結果、文字列 "username:password" base64がエンコードされた次のヘッダーが生成されます。

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Chrome 55とFirefox 50で基本認証をテストし、基本認証情報が実際にサーバーとネゴシエートされていることを確認しました(これはSafariでは機能しない可能性があります)。

基本的な認証の回答をしてくれたDmitry Frankに感謝


38
私は同じ問題に遭遇しました。これらの標準があまり統合されていないことは残念です。彼らはWebSockets APIの要件(WebSocketsとXHRは関連しているため)を見つけるためにXHR APIを調べることを期待しますが、アイランド自体でAPIを開発しているだけのようです。
eleotlecram

4
@ eleotlecram、HyBiワーキンググループに参加して提案してください。グループは一般に公開されており、プロトコルの後続のバージョンのための進行中の作業があります。
カナカ

5
@Charlie:サーバーを完全に制御する場合、それは1つのオプションです。へのより一般的なアプローチは、通常のHTTPサーバーからチケット/トークンを生成し、クライアントにチケット/トークンを(Webソケットパスのクエリ文字列として、または最初のWebソケットメッセージとして)送信させることです。次に、WebSocketサーバーはチケット/トークンが有効であること(有効期限が切れていない、まだ使用されていない、作成時と同じIPからのものなど)を検証します。また、ほとんどのWebSocketクライアントは基本認証をサポートしていると思います(ただし、これでは不十分な場合があります)。さらに詳しい情報:devcenter.heroku.com/articles/websocket-security
kanaka

3
仕様によると思います。実装は意図的にHTTPから借用しているように見えますが、設計上、可能な限りそれらを分離してください。仕様のテキストは続きます:「しかし、デザインはWebSocketをHTTPに制限せず、将来の実装ではプロトコル全体を再発明せずに専用ポートでより簡単なハンドシェイクを使用できます。この最後のポイントは重要です。インタラクティブメッセージングのトラフィックパターンが標準のHTTPトラフィックと厳密には一致せず、一部のコンポーネントに異常な負荷を引き起こす可能性があります。」

3
残念ながら、これはEdgeでは機能しないようです。ありがとう、MS:/
sibbl

40

代替ソリューションの詳細ですが、最新のブラウザはすべて、接続と一緒にドメインCookieを送信するため、以下を使用します。

var authToken = 'R3YKZFKBVi';

document.cookie = 'X-Authorization=' + authToken + '; path=/';

var ws = new WebSocket(
    'wss://localhost:9000/wss/'
);

リクエスト接続ヘッダーで終わります:

Cookie: X-Authorization=R3YKZFKBVi

1
これは、接続時にアクセストークンを渡す最良の方法のようです。現時点では。
cammil

1
WSサーバーURIがクライアントURIと異なる場合はどうなりますか?
デンマークの

35

HTTP Authorizationヘッダーの問題は、以下で対処できます。

var ws = new WebSocket("ws://username:password@example.com/service");

次に、提供されたusernameおよびを使用して、適切な基本認証HTTPヘッダーが設定されpasswordます。基本認証が必要な場合は、これで準備は完了です。


Bearerただし、使用したいので、次のトリックを使用しました。次のようにサーバーに接続します。

var ws = new WebSocket("ws://my_token@example.com/service");

そして、サーバー側の私のコードが空でないユーザー名と空のパスワードを持つBasic Authorizationヘッダーを受信すると、ユーザー名をトークンとして解釈します。


12
私はあなたが提案した解決策を試しています。しかし、リクエストに追加されているAuthorizationヘッダーを確認できません。Chrome V56、Firefox V51.0などのさまざまなブラウザーを使用して試してみました。ローカルホストでサーバーを実行しています。したがって、websocketのURLは "ws:// myusername:mypassword @ localhost:8080 / mywebsocket"です。何が間違っているのでしょうか?ありがとう
LearnToLive 2017年

4
urlを介してトークンを転送しても安全ですか?
メルガソフ2017年

2
ユーザー名がログに記録される可能性があるため、トークンとしての空/無視されたユーザー名と空ではないパスワードの方が良い場合があります。
AndreKR 2017年

9
@LearnToLiveに同意します-これをwss(例:)で使用し、サーバー側にヘッダーwss://user:password@myhost.com/wsがありませんAuthorization(Chromeバージョン60を使用)
user9645

6
@LearnToLiveおよび@ user9645と同じ問題があります。URIがwss://user:pass@hostフォーマットになっている場合、chromeもfirefoxも認証ヘッダーを追加しません。これはブラウザでサポートされていませんか、それともハンドシェイクに問題がありますか?
David Kaczynski、2018

19

ヘッダーを追加することはできませんが、接続時にサーバーに値を渡すだけでよい場合は、URLにクエリ文字列の部分を指定できます。

var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");

そのURLは有効ですが、もちろん-解析するにはサーバーコードを変更する必要があります。


14
この解決策には注意が必要です。クエリ文字列が傍受されたり、プロキシにログインしたりする可能性があるため、この方法で機密情報(ユーザー/パスワード/認証トークン)を渡すことは十分に安全ではありません。
2017

5
@NirとWSSを併用すると、クエリ文字列はおそらく安全になるはずです
Sebastien Lorber

8
wsはプレーンテキストです。wsプロトコルを使用すると、何でも傍受できます。
Gabriele Carioli

1
@SebastienLorber暗号化されていないクエリ文字列を使用するのは安全ではありません。これはHTTPSにも当てはまりますが、「ws:// ...」プロトコルが使用されているため、実際には問題になりません。
Lu4

5
LU4 @クエリ文字列は暗号化されますが、機密データを追加するためではない他の理由のホストがあるURLクエリパラメータとしてstackoverflow.com/questions/499591/are-https-urls-encrypted/...blog.httpwatch.com/2009 /

16

JavaScript WebSockets APIを使用してWebSockets接続を確立する場合は、カスタムヘッダーを送信できません。Subprotocols2番目のWebSocketクラスコンストラクターを使用してヘッダーを使用できます。

var ws = new WebSocket("ws://example.com/service", "soap");

Sec-WebSocket-Protocolサーバー上のキーを使用してサブプロトコルヘッダーを取得できます。

また、制限もあります。サブプロトコルヘッダーの値にカンマ(,)を含めることはできません。


1
Jwtにカンマを含めることができますか?
CESCO 2016年

1
私はそうは思いません。JWTは、それぞれがピリオドで区切られた3つのbase64エンコードされたペイロードで構成されています。私はこれがカンマの可能性を除外すると信じています。
BillyBBone 2017年

2
私はこれを実装し、それは動作します-変な感じがします。ありがとう
BatteryAcid 2017

3
Sec-WebSocket-Protocolヘッダーをヘッダーの代わりとして使用することを提案していますAuthorizationか?
rrw

13

Authorizationヘッダーを送信することはできません。

トークンクエリパラメータのアタッチはオプションです。ただし、状況によっては、メインのログイントークンをプレーンテキストでクエリパラメーターとして送信することが望ましくない場合があります。これは、ヘッダーを使用するよりも不透明であり、最終的にだれがログに記録することになるためです。これによりセキュリティの問題が発生する場合は、代替手段として、Webソケット用だけにセカンダリJWTトークンを使用します。

このJWTを生成するためのRESTエンドポイントを作成しますもちろん、これには、プライマリヘッダートークンで認証されたユーザーのみがアクセスできます(ヘッダー経由で送信されます)。WebソケットJWTは、ログイントークンとは異なる方法で構成できます。たとえば、タイムアウトを短くして、アップグレードリクエストのクエリパラメータとして送信する方が安全です。

でSockJS eventbusHandlerを登録したのと同じルートに個別のJwtAuthHandlerを作成します。最初に認証ハンドラーが登録されていることを確認してください。これにより、データベースに対してWebソケットトークンを確認できます(JWTは何らかの形でバックエンドのユーザーにリンクされているはずです)。


これは、API Gateway Webソケット用に思いつくことができる唯一の安全なソリューションでした。SlackはRTM APIで同様のことを行い、タイムアウトは30秒です。
andrhamm

2

カナカの答えのおかげで、このように完全にハッキングしました。

クライアント:

var ws = new WebSocket(
    'ws://localhost:8080/connect/' + this.state.room.id, 
    store('token') || cookie('token') 
);

サーバー(この例ではKoa2を使用していますが、どこでも同じである必要があります):

var url = ctx.websocket.upgradeReq.url; // can use to get url/query params
var authToken = ctx.websocket.upgradeReq.headers['sec-websocket-protocol'];
// Can then decode the auth token and do any session/user stuff...

4
これは、クライアントが1つ以上の特定のプロトコルを要求することになっているセクションでトークンを渡すだけではありませんか?私はこれを機能させることもできますが、問題はありませんが、これを行わず、onOpen()で認証トークンが送信されるまでMotesが提案してブロックすることにしました。プロトコルリクエストヘッダーのオーバーロードは私には間違っているように見えます。私のAPIは一般向けであるため、私のAPIのコンシューマーにとっては少し混乱するでしょう。
ジェイ

0

私の場合:

  • 本番WSサーバーに接続したいwww.mycompany.com/api/ws...
  • 実際の資格情報(セッションCookie)を使用しています...
  • ローカルページ(localhost:8000)から。

設定 document.cookie = "sessionid=foobar;path=/"ドメインが一致しないため、は役に立ちません。

解決策

に追加127.0.0.1 wsdev.company.com/etc/hostsます。

このようにして、有効なサブドメインからmycompany.com接続しwww.mycompany.com/api/wsているときに、ブラウザは接続時にからCookieを使用しますwsdev.company.com


-1

私の状況では(Azure Time Series Insights wss://)

ReconnectingWebsocketラッパーを使用して、簡単なソリューションでヘッダーの追加を実現できました。

socket.onopen = function(e) {
    socket.send(payload);
};

この場合のペイロードは次のとおりです。

{
  "headers": {
    "Authorization": "Bearer TOKEN",
    "x-ms-client-request-id": "CLIENT_ID"
}, 
"content": {
  "searchSpan": {
    "from": "UTCDATETIME",
    "to": "UTCDATETIME"
  },
"top": {
  "sort": [
    {
      "input": {"builtInProperty": "$ts"},
      "order": "Asc"
    }], 
"count": 1000
}}}

-3

技術的には、プロトコルアップグレードフェーズの前に、接続機能を介してこれらのヘッダーを送信します。これはnodejsプロジェクトで私のために働きました:

var WebSocketClient = require('websocket').client;
var ws = new WebSocketClient();
ws.connect(url, '', headers);

3
これは、npm(ノード用)のWebSocketクライアント用です。npmjs.com/package/websocket全体的に、これは私が探しているものとまったく同じですが、ブラウザ内です。
arnuschky 2017

1
このheadersパラメーターはWebSocketプロトコルレイヤーにあり、問題はHTTPヘッダーに関するものであるため、反対票を投じています。
Toilal 2017年

headersnull、またはリクエストとともに送信する追加の任意のHTTPリクエストヘッダーを指定するオブジェクトのいずれかである必要があります。」WebSocketClient.mdから; したがって、headersここはHTTPレイヤーです。
momocow 2018年

また、カスタムヘッダーを提供したい人は、心の中の関数シグネチャおく必要があるconnectと説明した方法を、connect(requestUrl, requestedProtocols, [[[origin], headers], requestOptions])すなわち、headers一緒に提供されるべきであるrequestOptions、例えば、ws.connect(url, '', headers, null)originこの場合、無視できるのは文字列のみです。
momocow

-4

オブジェクト内の3番目のパラメーター(オプション)で、ヘッダーをKey-Valueとして渡すことができます。認可トークンの例。プロトコル(2番目のパラメーター)をnullのままにします。

ws = new WebSocket( 'ws:// localhost'、null、{headers:{Authorization:token}})

編集:このアプローチはnodejsライブラリでのみ機能し、標準のブラウザー実装では機能しないようです。一部の人にとっては便利かもしれないので、そのままにしておきます。


少しの間期待していた。WebSocket ctorの3番目のパラメーターを受け取るオーバーロードはないようです。
Levitikon

wscatコードからアイデアを得ました。github.com/websockets/wscat/blob/master/bin/wscat 261行目では、wsパッケージを使用しています。これは標準的な使い方だと思いました。
Nodens
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.