Websockets istemci API'sındaki HTTP üstbilgileri


204

Bunu destekleyen herhangi bir HTTP üstbilgisi istemcisi ile websocket istemcinize özel HTTP üstbilgileri eklemenin kolay olduğu anlaşılıyor, ancak JSON API ile nasıl yapılacağını bulamıyorum.

Yine de, bu başlıklarda spesifikasyonda destek olması gerektiği görülmektedir .

Bunu nasıl başaracağına dair bir ipucu var mı?

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

Özellikle, bir HTTP Yetkilendirme başlığı gönderebilmem gerekiyor.


14
Ben iyi bir çözüm WebSocket izinsiz bağlanmak için izin, ama sonra engellemek ve onopen olayında yetkilendirme bilgilerini iletecek webSocket yetkisini almak için sunucuda beklemek olduğunu düşünüyorum.
Motes

@Motes'un önerisi en uygun seçenek gibi görünüyor. OnOpen'den yetkilendirme yanıtına dayalı olarak soketi kabul etmenizi / reddetmenizi sağlayan bir yetkilendirme çağrısı yapmak çok kolaydı. Başlangıçta Sec-WebSocket-Protocol başlığında auth token göndermeye çalıştım ama bu bir hack gibi geliyor.
BatteryAcid

@Motes Merhaba, "sunucuda engelle ve bekle" bölümünü açıklayabilir misiniz? "auth" mesajı olana kadar hiçbir mesajı işlememe gibi bir şey mi demek istiyorsun?
Himal

@Himal, evet sunucu tasarımı bağlantının başlangıcında veri göndermemeli veya yetkilendirme dışında başka bir veri kabul etmemelidir.
Motes

@Motes Cevabınız için teşekkürler. Engelleme kısmı biraz kafam karıştı, çünkü ilk isteğimi engelleyemediğime göre connect. Arka uçta Django kanalları kullanıyorum ve connectolaydaki bağlantıyı kabul edecek şekilde tasarladım . daha sonra receiveetkinlikte bir "is_auth" bayrağı koyar (geçerli bir kimlik doğrulama mesajı görürse). is_auth bayrağı ayarlanmamışsa ve bir yetkilendirme mesajı değilse, bağlantıyı kapatır.
Himal

Yanıtlar:


214

Güncelleme 2x

Kısa cevap: Hayır, yalnızca yol ve protokol alanı belirtilebilir.

Daha uzun cevap:

JavaScript WebSockets API'sında , istemcinin / tarayıcının göndereceği ek başlıkları belirtmek için herhangi bir yöntem yoktur . HTTP yolu ("GET / xyz") ve protokol başlığı ("Sec-WebSocket-Protocol") WebSocket yapıcısında belirtilebilir.

Sec-WebSocket-Protocol üstbilgisi (bazen websocket'a özgü kimlik doğrulamasında kullanılmak üzere genişletilir), isteğe bağlı ikinci bağımsız değişkenten WebSocket yapıcısına oluşturulur:

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

Yukarıdaki sonuçlar aşağıdaki başlıklarla sonuçlanır:

Sec-WebSocket-Protocol: protocol

ve

Sec-WebSocket-Protocol: protocol1, protocol2

WebSocket kimlik doğrulaması / yetkilendirmesi elde etmek için yaygın bir desen, WebSocket istemcisini barındıran sayfanın sunucudan bir bilet istediği ve daha sonra bu bileti WebSocket bağlantı kurulumu sırasında URL / sorgu dizesinde, protokol alanında geçirdiği bir biletleme sistemi uygulamaktır. veya bağlantı kurulduktan sonraki ilk mesaj olarak gereklidir. Bu durumda sunucu, bağlantının yalnızca bilet geçerliyse devam etmesine izin verir (var, henüz kullanılmamış, bilet maçlarında istemci IP'si kodlanmış, bilette zaman damgası yeni olmuşsa, vb.). WebSocket güvenlik bilgilerinin bir özeti: https://devcenter.heroku.com/articles/websocket-security

Temel kimlik doğrulama eskiden bir seçenekti, ancak bu kullanımdan kaldırılmıştır ve modern tarayıcılar, belirtilse bile üstbilgiyi göndermez.

Temel Yetkilendirme Bilgisi (Kullanımdan kaldırıldı) :

Yetkilendirme başlığı, WebSocket URI'sının kullanıcı adı ve şifre (veya yalnızca kullanıcı adı) alanından oluşturulur:

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

Yukarıdaki sonuç, "kullanıcı adı: şifre" base64 kodlu dizeyle aşağıdaki üstbilgiyle sonuçlanır:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Chrome 55 ve Firefox 50'de temel yetkilendirmeyi test ettim ve temel yetkilendirme bilgilerinin gerçekten sunucu ile pazarlık edildiğini doğruladım (bu Safari'de çalışmayabilir).

Temel kimlik yanıtı için Dmitry Frank'a teşekkürler


39
Ben de aynı problemle karşılaştım. Bu standartların o kadar zayıf bütünleşmesi çok kötü. WebSockets API'sı için gereksinimleri (WebSockets ve XHR ilişkili olduğu için) bulmak için XHR API'sine bakmalarını beklersiniz, ancak görünüşe göre sadece bir adada API'yı geliştiriyorlar.
eleotlecram

4
@eleotlecram, HyBi çalışma grubuna katılın ve önerin. Grup halka açık ve protokolün sonraki sürümleri için devam eden çalışmalar var.
kanaka

5
@Charlie: Eğer sunucuyu tamamen kontrol ediyorsanız, bu bir seçenektir. En yaygın yaklaşım, normal HTTP sunucunuzdan bir bilet / jeton oluşturmak ve daha sonra istemcinin bilet / jetonu göndermesini (websocket yolunda bir sorgu dizesi olarak veya ilk websocket iletisi olarak) istemektir. Websocket sunucusu daha sonra bilet / jetonun geçerli olduğunu doğrular (süresi dolmamış, henüz kullanılmamış, oluşturulduğu zamanki IP'den gelen vb.). Ayrıca, çoğu websockets istemcisinin temel yetkilendirmeyi desteklediğine inanıyorum (yine de sizin için yeterli olmayabilir). Daha fazla bilgi: devcenter.heroku.com/articles/websocket-security
kanaka

3
Sanırım tasarım gereği. Uygulamanın kasıtlı olarak HTTP'den ödünç alındığı, ancak tasarımla mümkün olduğunca ayrı tutulduğu izlenimi altındayım. Spesifikasyondaki metin şöyle devam ediyor: "Bununla birlikte, tasarım WebSocket'i HTTP ile sınırlamaz ve gelecekteki uygulamalar, tüm protokolü yeniden icat etmeden özel bir bağlantı noktası üzerinde daha basit bir el sıkışma kullanabilir. standart HTTP trafiğiyle yakından eşleşmez ve bazı bileşenlerde olağandışı yükler oluşturabilir. "

3
Ne yazık ki bu Edge'de çalışmıyor gibi görünüyor. Teşekkürler, MS: /
sibbl

42

Daha fazla alternatif çözüm, ancak tüm modern tarayıcılar alan çerezlerini bağlantıyla birlikte gönderir, bu nedenle:

var authToken = 'R3YKZFKBVi';

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

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

Sonunda istek bağlantı üstbilgileri bulunur:

Cookie: X-Authorization=R3YKZFKBVi

1
Bu, bağlantıda erişim belirteçlerini geçirmenin en iyi yolu gibi görünüyor. Şu an.
cammil

2
WS sunucusu URI'sı istemci URI'sinden farklıysa ne olur?
Danimarka

1
@Danish Peki o zaman bu işe yaramıyor, çünkü diğer etki alanları için istemci ayarları ayarlayamıyorsunuz
Tofandel

1
Ancak, ilgili yolda bir oturum çerezi ayarlayan bir HTTP hizmeti ayarlayabilir ve web soketine başlamadan önce bunu çağırabilirsiniz. Arayın, söyleyin https://example.com/loginve yanıtın bir çerez ayarlamasını sağlayın, /wssardından new WebSocket("wss://example.com/wss")ilgili çerezle el sıkışma isteğini başlatacaktır. Devtools'un çerezi göstermeyebileceğini, ancak yine de gönderilmesi gerektiğini unutmayın.
Coderer

34

HTTP Yetkilendirme başlığı sorunu aşağıdakilerle giderilebilir:

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

Ardından, sağlanan usernameve ile uygun bir Temel Yetkilendirme HTTP üst bilgisi ayarlanır password. Temel Yetkilendirmeye ihtiyacınız varsa, hazırsınız demektir.


BearerAncak kullanmak istiyorum ve aşağıdaki hile başvurdu: Sunucuya aşağıdaki gibi bağlanır:

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

Sunucu tarafındaki kodum boş olmayan kullanıcı adı ve boş parola ile Temel Yetkilendirme başlığını aldığında, kullanıcı adını bir belirteç olarak yorumlar.


12
Önerdiğiniz çözümü deniyorum. Ancak Yetkilendirme başlığının isteğime eklendiğini göremiyorum. Chrome V56, Firefox V51.0 gibi farklı tarayıcıları kullanarak denedim. Sunucumu localhost üzerinde çalıştırıyorum. yani websocket url'si "ws: // kullanıcı adı: mypassword @ localhost: 8080 / mywebsocket" olur. Neyin yanlış olabileceği hakkında bir fikrin var mı? Teşekkürler
LearnToLive

4
Jetonu URL üzerinden aktarmak güvenli mi?
Mergasov

9
@LearnToLive ile aynı fikirdeyim - bunu wss (örn. wss://user:password@myhost.com/ws) Authorizationİle kullandım ve sunucu tarafında başlık yok (Chrome 60 sürümünü kullanarak)
user9645

6
@LearnToLive ve @ user9645 ile aynı sorunu var; URI wss://user:pass@hostbiçimindeyken ne krom ne de firefox yetkilendirme başlığını eklemez . Bu tarayıcılar tarafından desteklenmiyor mu, yoksa el sıkışmasında bir sorun mu var?
David Kaczynski

4
Bu url'lerin http://username:password@example.comkullanımı amortismana tabi tutulur. developer.mozilla.org/en-US/docs/Web/HTTP/Authentication . Belki de websockets ile çalışmaz nedeni budur!
Dachstein

19

Üstbilgi ekleyemezsiniz, ancak bağlantı anında sunucuya değerleri iletmeniz gerekiyorsa, url'de bir sorgu dizesi parçası belirtebilirsiniz:

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

Bu URL geçerlidir, ancak - elbette - ayrıştırmak için sunucu kodunuzu değiştirmeniz gerekir.


15
Bu çözüme dikkat etmeniz gerekiyorsa, sorgu dizesi ele geçirilebilir, proxy'lerde oturum açmış vb.
Nir

5
@ WSS ile sorgu dizisi muhtemelen güvenli olmalı
Sebastien Lorber

8
ws düz metindir. Ws protokolü kullanılarak her şey ele geçirilebilir.
Gabriele Carioli

1
@SebastienLorber şifrelenmemiş sorgu dizesini kullanmak güvenli değildir, aynı HTTPS için de geçerlidir, ancak "ws: // ..." protokolü kullanıldığından, gerçekten önemli değildir.
Lu4

5
@ Lu4 sorgu dizesi şifrelenmiştir, ancak URL verilerini parametre sorgusu olarak hassas verilerin eklememesinin başka nedenleri de vardır stackoverflow.com/questions/499591/are-https-urls-encrypted/… & blog.httpwatch.com/2009 /

17

JavaScript WebSockets API'sını kullanarak WebSockets bağlantısı kurmak istediğinizde özel başlık gönderemezsiniz. Subprotocolsİkinci WebSocket sınıfı yapıcısını kullanarak başlıkları kullanabilirsiniz :

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

ve sonra Sec-WebSocket-Protocolsunucudaki anahtarı kullanarak Subprotocols üstbilgilerini alabilirsiniz .

Ayrıca bir sınırlama vardır, Subprotocols başlık değerleriniz virgül ( ,) içeremez !


1
Jwt virgül içerebilir mi?
CESCO

1
Ben inanmıyorum. JWT, her biri bir nokta ile ayrılmış üç temel 64 kodlu faydalı yükten oluşur. Bunun virgül olasılığını ortadan kaldırdığına inanıyorum.
BillyBBone

2
Bunu uyguladım ve işe yarıyor - sadece garip geliyor. teşekkürler
BatteryAcid

3
Sec-WebSocket-Protocolüstbilgiyi , üstbilgiye alternatif olarak kullanmamızı mı öneriyorsunuz Authorization?
rrw

14

Gönderme Yetkilendirmesi başlığı mümkün değil.

Belirteç sorgusu parametresi eklemek bir seçenektir. Bununla birlikte, bazı durumlarda, ana giriş simgenizi bir sorgu parametresi olarak düz metin olarak göndermek istenmeyen bir durum olabilir, çünkü bir başlık kullanmaktan daha opaktır ve kimin nerede olduğunu kaydeder. Bu sizin için güvenlik endişeleri doğurursa , bir alternatif sadece web soketi şeyler için ikincil bir JWT jetonu kullanmaktır .

Bu JWT'yi oluşturmak için bir REST bitiş noktası oluşturun ; bu, elbette yalnızca birincil giriş simgenizle kimliği doğrulanmış kullanıcılar tarafından erişilebilir (başlık üzerinden aktarılır). Web soketi JWT, oturum açma simgenizden farklı bir şekilde yapılandırılabilir, örneğin daha kısa bir zaman aşımı ile, bu nedenle yükseltme isteğinizin sorgu parametresi olarak göndermek daha güvenlidir.

SockJS eventbusHandler'ı kaydettiğiniz rota için ayrı bir JwtAuthHandler oluşturun . İlk önce kimlik doğrulama işleyicinizin kayıtlı olduğundan emin olun, böylece web soket belirtecini veritabanınıza göre kontrol edebilirsiniz (JWT bir şekilde arka uçtaki kullanıcıyla bağlantılı olmalıdır).


API Ağ Geçidi websockets için bulabildiğim tek güvenli çözüm buydu. Slack, RTM API'lerine benzer bir şey yapar ve 30 saniyelik bir zaman aşımına sahiptir.
andrhamm

2

Tamamen böyle kırdı, kanaka cevabı sayesinde.

Müşteri:

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

Sunucu (bu örnekte Koa2 kullanılıyor, ancak her yerde benzer olmalıdır):

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
Bu, jetonunuzu müşterinizin bir veya daha fazla özel protokol istemesi gereken bölümde geçmiyor mu? Ben bu çalışma, hiçbir sorun da alabilirsiniz, ama ben bunu yapmamaya karar verdi ve daha ziyade Motes önerilen ve auth token onOpen () gönderilinceye kadar engellemek. Protokol isteği başlığının aşırı yüklenmesi benim için yanlış görünüyor ve API'm kamu tüketimi için olduğundan, API'mın tüketicileri için biraz kafa karıştırıcı olacağını düşünüyorum.
Jay

0

Benim olayım:

  • Bir üretim WS sunucusuna bağlanmak istiyorum www.mycompany.com/api/ws...
  • gerçek kimlik bilgileri (bir oturum çerezi) kullanılıyor ...
  • yerel bir sayfadan ( localhost:8000).

document.cookie = "sessionid=foobar;path=/"Alanlar eşleşmediğinden ayar yardımcı olmaz.

Çözüm :

Ekle 127.0.0.1 wsdev.company.comiçin /etc/hosts.

Tarayıcınız çerezleri kullanır Bu şekilde mycompany.combağlanırken www.mycompany.com/api/wsgeçerli bir alt alan adından bağlanıyorsanız olarak wsdev.company.com.


-1

Benim durumumda (Azure Time Series Insights wss: //)

ReconnectingWebsocket sarıcısını kullanarak ve basit bir çözümle başlık eklemeyi başardı:

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

Bu durumda yükün nerede olduğu:

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

Teknik olarak, bu başlıkları protokol yükseltme aşamasından önce connect işlevi aracılığıyla göndereceksiniz. Bu benim için bir nodejsprojede çalıştı :

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

3
Bu npm cinsinden websocket istemcisi içindir (düğüm için). npmjs.com/package/websocket Genel olarak bu tam olarak aradığım şey olurdu, ancak tarayıcıda.
arnuschky

1
Bu heads parametresi WebSocket protokol katmanında içeri girdiği ve soru HTTP üstbilgileriyle ilgili olduğu için indirildi.
Toilal

" headersnull veya istekle birlikte gönderilecek isteğe bağlı HTTP istek üstbilgilerini belirten bir nesne olmalıdır." adlı WebSocketClient.md ; bu nedenle, headersburada HTTP katmanı.
momocow

Ayrıca, özel başlıklar sağlamak isteyen herkes akılda işlevi imza tutmalı connectolarak tanımlanan yöntemle, connect(requestUrl, requestedProtocols, [[[origin], headers], requestOptions])yani headersbirlikte sağlanmalıdır requestOptions, örneğin, ws.connect(url, '', headers, null). originBu durumda yalnızca dize yok sayılabilir.
momocow

-3

Üstbilgileri, bir nesnenin içindeki üçüncü parametrede (seçenekler) anahtar / değer çifti olarak iletebilirsiniz . Yetkilendirme belirteci örneği. Protokolü (ikinci parametre) null olarak bıraktı

ws = yeni WebSocket ('ws: // localhost', null, {headers: {Authorization: token}})

Düzenleme: Bu yaklaşım sadece standart tarayıcı uygulaması ile değil, nodejs kütüphanesi ile çalışır gibi görünüyor. Onu bırakmak, çünkü bazı insanlar için yararlı olabilir.


Bir saniyeliğine umutlarım vardı. WebSocket ctor'da 3. param alan bir aşırı yüklenme yok gibi görünüyor.
Levitikon

Wscat kodundan fikir aldım. ws paketini kullanan github.com/websockets/wscat/blob/master/bin/wscat line 261. Bunun standart bir kullanım olduğunu düşündüm.
Nodens
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.