Intestazioni HTTP nell'API client di Websocket


201

Sembra che sia facile aggiungere intestazioni HTTP personalizzate al tuo client websocket con qualsiasi client di intestazione HTTP che supporti questo, ma non riesco a trovare come farlo con l'API JSON.

Tuttavia, sembra che ci dovrebbe essere supporto queste intestazioni nelle specifiche .

Qualcuno ha idea di come raggiungerlo?

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

In particolare, devo essere in grado di inviare un'intestazione di autorizzazione HTTP.


14
Penso che una buona soluzione sia consentire al WebSocket di connettersi senza autorizzazione, ma quindi bloccare e attendere sul server la ricezione dell'autorizzazione dal webSocket che trasmetterà le informazioni di autorizzazione nel suo evento onopen.
Motes,

Il suggerimento di @Motes sembra essere la soluzione migliore. È stato molto semplice effettuare una chiamata di autorizzazione da onOpen che consente di accettare / rifiutare il socket in base alla risposta di autorizzazione. Inizialmente ho tentato di inviare il token di autenticazione nell'intestazione Sec-WebSocket-Protocol ma sembra un hack.
BatteryAcid

@Motes Ciao, potresti spiegare la parte "blocca e aspetta sul server"? intendi qualcosa come non elaborare alcun messaggio finché non c'è un messaggio "auth"?
Himal,

@Himal, sì, la progettazione del server non deve inviare dati o accettare dati diversi dall'autorizzazione all'inizio della connessione.
Motes,

@Motes Grazie per la risposta. Sono stato un po 'confuso dalla parte bloccante, a causa della mia comprensione non è possibile bloccare la connectrichiesta iniziale . Sto usando i canali Django sul back-end e l'ho progettato per accettare la connessione connectall'evento. quindi imposta un flag "is_auth" receivenell'evento (se vede un messaggio di autenticazione valido). se il flag is_auth non è impostato e non è un messaggio di autenticazione, chiude la connessione.
Himal,

Risposte:


212

Aggiornato 2x

Risposta breve: No, è possibile specificare solo il campo percorso e protocollo.

Risposta più lunga:

Non esiste alcun metodo nell'API WebSocket JavaScript per specificare ulteriori intestazioni da inviare al client / browser. Il percorso HTTP ("GET / xyz") e l'intestazione del protocollo ("Sec-WebSocket-Protocol") possono essere specificati nel costruttore WebSocket.

L'intestazione Sec-WebSocket-Protocol (che a volte viene estesa per essere utilizzata nell'autenticazione specifica di websocket) viene generata dal secondo argomento facoltativo al costruttore WebSocket:

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

Quanto sopra risulta nelle seguenti intestazioni:

Sec-WebSocket-Protocol: protocol

e

Sec-WebSocket-Protocol: protocol1, protocol2

Un modello comune per ottenere l'autenticazione / autorizzazione WebSocket è l'implementazione di un sistema di ticketing in cui la pagina che ospita il client WebSocket richiede un ticket dal server e quindi passa questo ticket durante l'impostazione della connessione WebSocket nella stringa URL / query, nel campo protocollo, o richiesto come primo messaggio dopo aver stabilito la connessione. Il server consente quindi di continuare la connessione solo se il ticket è valido (esiste, non è già stato utilizzato, l'IP client codificato nelle corrispondenze dei ticket, il timestamp nel ticket è recente, ecc.). Ecco un riepilogo delle informazioni sulla sicurezza di WebSocket: https://devcenter.heroku.com/articles/websocket-security

L'autenticazione di base era in precedenza un'opzione, ma questo è stato deprecato e i browser moderni non inviano l'intestazione anche se è stata specificata.

Informazioni di base sull'autorizzazione (obsoleto) :

L'intestazione di autorizzazione viene generata dal campo nome utente e password (o solo nome utente) dell'URI WebSocket:

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

Quanto sopra risulta nella seguente intestazione con la stringa "nomeutente: password" codificata base64:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Ho testato l'autent di base in Chrome 55 e Firefox 50 e verificato che le informazioni sull'autent di base siano effettivamente negoziate con il server (ciò potrebbe non funzionare in Safari).

Grazie a Dmitry Frank per la risposta autentica di base


38
Ho riscontrato lo stesso problema. Peccato che questi standard siano così scarsamente integrati. Ti aspetteresti che guardino l'API XHR per trovare i requisiti (poiché WebSocket e XHR sono correlati) per l'API WebSocket, ma sembra che stiano solo sviluppando l'API su un'isola da sola.
eleotlecram

4
@eleotlecram, unisciti al gruppo di lavoro HyBi e proponilo. Il gruppo è aperto al pubblico e sono in corso lavori per le versioni successive del protocollo.
Kanaka

5
@Charlie: se controlli completamente il server, questa è un'opzione. L'approccio più comune è generare un ticket / token dal normale server HTTP e quindi fare in modo che il client invii il ticket / token (come stringa di query nel percorso del websocket o come primo messaggio del websocket). Il server websocket verifica quindi che il ticket / token sia valido (non è scaduto, non è già stato utilizzato, proviene dallo stesso IP della creazione, ecc.). Inoltre, credo che la maggior parte dei client WebSocket supporti l'autent di base (potrebbe non essere sufficiente per te). Maggiori informazioni: devcenter.heroku.com/articles/websocket-security
Kanaka

3
Immagino che sia di design. Ho l'impressione che l'implementazione stia prendendo in prestito intenzionalmente da HTTP, ma li tiene separati il ​​più possibile dalla progettazione. Il testo nella specifica continua: "Tuttavia, il design non limita WebSocket a HTTP e le future implementazioni potrebbero utilizzare una stretta di mano più semplice su una porta dedicata senza reinventare l'intero protocollo. Quest'ultimo punto è importante perché i modelli di traffico della messaggistica interattiva fanno non corrisponde strettamente al traffico HTTP standard e può indurre carichi insoliti su alcuni componenti. "

3
Sfortunatamente questo non sembra funzionare in Edge. Grazie, MS: /
sibbl,

40

Più di una soluzione alternativa, ma tutti i browser moderni inviano i cookie di dominio insieme alla connessione, quindi utilizzando:

var authToken = 'R3YKZFKBVi';

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

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

Termina con le intestazioni di connessione richieste:

Cookie: X-Authorization=R3YKZFKBVi

1
Questo sembra il modo migliore per passare i token di accesso durante la connessione. Al momento.
cammil

2
cosa succede se l'URI del server WS è diverso dall'URI del client?
Danese

@Danish Bene, allora non funziona, dal momento che non è possibile impostare cookie per altri domini lato client
Tofandel

35

Il problema dell'intestazione dell'autorizzazione HTTP può essere risolto con quanto segue:

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

Quindi, verrà impostata un'intestazione HTTP di autorizzazione di base corretta con il fornito usernamee password. Se hai bisogno dell'autorizzazione di base, sei pronto.


Voglio usare Bearercomunque, e ho fatto ricorso al seguente trucco: mi collego al server come segue:

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

E quando il mio codice sul lato server riceve l'intestazione dell'autorizzazione di base con nome utente non vuoto e password vuota, allora interpreta il nome utente come token.


12
Sto provando la soluzione suggerita da te. Ma non riesco a vedere l'intestazione dell'autorizzazione aggiunta alla mia richiesta. L'ho provato utilizzando diversi browser, ad es. Chrome V56, Firefox V51.0. Sto eseguendo il mio server sul mio localhost. quindi l'URL del websocket è "ws: // myusername: mypassword @ localhost: 8080 / mywebsocket". Qualche idea di cosa potrebbe essere sbagliato? Grazie
LearnToLive

4
È sicuro trasferire token tramite url?
Mergasov,

2
Nome utente vuoto / ignorato e password non vuota come token potrebbero essere migliori perché i nomi utente potrebbero essere registrati.
AndreKR,

9
Sono d'accordo con @LearnToLive - L'ho usato con wss (ad es. wss://user:password@myhost.com/ws) E non ho avuto nessuna Authorizationintestazione sul lato server (usando Chrome versione 60)
user9645

6
Ho lo stesso problema di @LearnToLive e @ user9645; né Chrome né Firefox stanno aggiungendo l'intestazione dell'autorizzazione quando l'URI è nel wss://user:pass@hostformato. Questo non è supportato dai browser o qualcosa non funziona con l'handshake?
David Kaczynski,

19

Non è possibile aggiungere intestazioni ma, se è necessario solo passare valori al server al momento della connessione, è possibile specificare una parte della stringa di query sull'URL:

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

Tale URL è valido ma, ovviamente, dovrai modificare il codice del server per analizzarlo.


14
è necessario fare attenzione con questa soluzione, la stringa di query può essere intercettata, proxy connessi ecc., quindi passare informazioni sensibili (utenti / password / token di autenticazione) in questo modo non sarà abbastanza sicuro.
Nir

5
@Nir con WSS la querystring dovrebbe essere probabilmente al sicuro
Sebastien Lorber

8
ws è un testo semplice. Usando il protocollo ws tutto può essere intercettato.
Gabriele Carioli

1
@SebastienLorber non è sicuro usare una stringa di query, non viene crittografato, lo stesso vale per HTTPS, ma poiché viene utilizzato il protocollo "ws: // ...", non importa.
Lu4

5
@ LU4 la stringa di query è crittografato, ma ci sono una serie di altri motivi per non aggiungere dati sensibili come parametri di ricerca URL stackoverflow.com/questions/499591/are-https-urls-encrypted/... & blog.httpwatch.com/2009 /

16

Non è possibile inviare un'intestazione personalizzata quando si desidera stabilire una connessione WebSocket utilizzando l'API WebSocket JavaScript. Puoi usare le Subprotocolsintestazioni usando il secondo costruttore della classe WebSocket:

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

e quindi è possibile ottenere le intestazioni dei protocolli secondari utilizzando la Sec-WebSocket-Protocolchiave sul server.

C'è anche una limitazione, i valori delle intestazioni dei sottoprotocolli non possono contenere una virgola ( ,)!


1
Un Jwt può contenere una virgola?
CESCO,

1
Io non ci credo. JWT è costituito da tre payload codificati in base64, ciascuno separato da un punto. Credo che ciò escluda la possibilità di una virgola.
BillyBBone,

2
Ho implementato questo e funziona - sembra strano. grazie
BatteryAcid,

3
stai suggerendo di usare l' Sec-WebSocket-Protocolintestazione come alternativa all'intestazione Authorization?
RRW

13

Non è possibile inviare l'intestazione di autorizzazione.

Il collegamento di un parametro di query token è un'opzione. Tuttavia, in alcune circostanze, potrebbe non essere desiderabile inviare il token di accesso principale in testo normale come parametro di query perché è più opaco rispetto all'utilizzo di un'intestazione e finirà per essere registrato da qualche parte. Se ciò solleva problemi di sicurezza per te, un'alternativa è utilizzare un token JWT secondario solo per le cose del socket web .

Creare un endpoint REST per generare questo JWT , a cui ovviamente gli utenti possono accedere solo con il tuo token di accesso principale (trasmesso tramite intestazione). Il web socket JWT può essere configurato in modo diverso rispetto al token di accesso, ad esempio con un timeout più breve, quindi è più sicuro inviare come parametro di query della richiesta di aggiornamento.

Creare un JwtAuthHandler separato per la stessa route su cui si registra SbusJS eventbusHandler . Assicurarsi che il gestore di autenticazione sia prima registrato, quindi è possibile verificare il token del socket Web rispetto al proprio database (il JWT dovrebbe essere in qualche modo collegato all'utente nel back-end).


Questa è stata l'unica soluzione sicura che ho potuto inventare per i websocket del gateway API. Slack fa qualcosa di simile con la sua API RTM e ha un timeout di 30 secondi.
Andrhamm,

2

Totalmente l'hackato in questo modo, grazie alla risposta di Kanaka.

Cliente:

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

Server (usando Koa2 in questo esempio, ma dovrebbe essere simile ovunque):

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
Questo non passa semplicemente il token nella sezione in cui il tuo client dovrebbe richiedere uno o più protocolli specifici? Posso farlo funzionare, anche nessun problema, ma ho deciso di non farlo e piuttosto fare ciò che Motes ha suggerito e bloccare fino a quando il token di autenticazione non viene inviato su onOpen (). Il sovraccarico dell'intestazione della richiesta del protocollo mi sembra sbagliato e poiché la mia API è destinata al consumo pubblico, penso che sarà un po 'confusa per i consumatori della mia API.
Jay,

0

Il mio caso:

  • Voglio collegarmi a un server WS di produzione a www.mycompany.com/api/ws ...
  • utilizzando credenziali reali (un cookie di sessione) ...
  • da una pagina locale ( localhost:8000).

L'impostazione document.cookie = "sessionid=foobar;path=/"non aiuta poiché i domini non corrispondono.

La soluzione :

Aggiungi 127.0.0.1 wsdev.company.coma /etc/hosts.

In questo modo il tuo browser utilizzerà i cookie da mycompany.comquando ti connetti a www.mycompany.com/api/wscome ti connetti da un sottodominio valido wsdev.company.com.


-1

Nella mia situazione (Azure Time Series Insights wss: //)

Utilizzando il wrapper ReconnectingWebsocket ed è stato in grado di ottenere l'aggiunta di intestazioni con una soluzione semplice:

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

Dove il payload in questo caso è:

{
  "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

Tecnicamente, invierai queste intestazioni tramite la funzione di connessione prima della fase di aggiornamento del protocollo. Questo ha funzionato per me in un nodejsprogetto:

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

3
Questo è per il client websocket in npm (per nodo). npmjs.com/package/websocket Nel complesso questo sarebbe esattamente quello che sto cercando, ma nel browser.
arnuschky,

1
È sottoposto a downgrade perché questo parametro delle intestazioni rientra nel livello del protocollo WebSocket e la domanda riguarda le intestazioni HTTP.
Toilal,

" headersdeve essere nullo o un oggetto che specifica ulteriori intestazioni di richiesta HTTP arbitrarie da inviare insieme alla richiesta." da WebSocketClient.md ; pertanto, il headersqui è il livello HTTP.
Momocow,

Inoltre, chiunque desideri fornire intestazioni personalizzate dovrebbe tenere a mente la firma della funzione del connectmetodo, descritta come connect(requestUrl, requestedProtocols, [[[origin], headers], requestOptions]), ad esempio, headersdovrebbe essere fornita insieme requestOptions, ad esempio, a ws.connect(url, '', headers, null). originIn questo caso è possibile ignorare solo la stringa.
Momocow,

-4

Puoi passare le intestazioni come valore-chiave nel terzo parametro (opzioni) all'interno di un oggetto. Esempio con token di autorizzazione. Lasciato il protocollo (secondo parametro) come nullo

ws = new WebSocket ('ws: // localhost', null, {headers: {Autorizzazione: token}})

Modifica: sembra che questo approccio funzioni solo con la libreria nodejs e non con l'implementazione standard del browser. Lasciandolo perché potrebbe essere utile per alcune persone.


Ho avuto le mie speranze per un secondo. Non sembra esserci un sovraccarico che prende un terzo parametro in WebSocket ctor.
Levitikon,

Ho avuto l'idea dal codice di wscat. github.com/websockets/wscat/blob/master/bin/wscat linea 261 che utilizza il pacchetto ws. Pensavo fosse un uso standard.
Nodens,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.