Tiêu đề HTTP trong API máy khách Websockets


200

Có vẻ như thật dễ dàng để thêm các tiêu đề HTTP tùy chỉnh vào ứng dụng khách websocket của bạn với bất kỳ ứng dụng khách tiêu đề HTTP nào hỗ trợ điều này, nhưng tôi không thể tìm thấy cách thực hiện với API JSON.

Tuy nhiên, có vẻ như cần phải hỗ trợ các tiêu đề này trong thông số kỹ thuật .

Bất cứ ai cũng có một đầu mối về làm thế nào để đạt được nó?

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

Cụ thể, tôi cần có thể gửi tiêu đề Ủy quyền HTTP.


14
Tôi nghĩ rằng một giải pháp tốt là cho phép WebSocket kết nối mà không cần ủy quyền, nhưng sau đó chặn và chờ trên máy chủ để nhận ủy quyền từ webSocket sẽ truyền thông tin ủy quyền trong sự kiện onopen của nó.
Mote

Gợi ý của @Motes dường như là phù hợp nhất. Rất dễ dàng để thực hiện cuộc gọi ủy quyền từ onOpen, cho phép bạn chấp nhận / từ chối ổ cắm dựa trên phản hồi ủy quyền. Ban đầu tôi đã cố gửi mã xác thực auth trong tiêu đề Sec-WebSocket-Protocol nhưng cảm giác đó giống như một vụ hack.
BatteryAcid

@Motes Xin chào, bạn có thể giải thích phần "chặn và chờ trên máy chủ" không? ý bạn là gì đó như không xử lý bất kỳ tin nhắn nào cho đến khi có tin nhắn "auth"?
Hy Lạp

@ Tối ưu, có, thiết kế máy chủ không được gửi dữ liệu hoặc chấp nhận bất kỳ dữ liệu nào khác ngoài ủy quyền khi bắt đầu kết nối.
Bỏ phiếu

@Motes Cảm ơn bạn đã trả lời. Tôi hơi bối rối bởi phần chặn, vì tôi hiểu rằng bạn không thể chặn connectyêu cầu ban đầu . Tôi đang sử dụng các kênh Django ở mặt sau và tôi đã thiết kế nó để chấp nhận kết nối trên connectsự kiện. sau đó nó đặt cờ "is_auth" trong receivesự kiện (nếu thấy thông báo xác thực hợp lệ). nếu cờ is_auth không được đặt và nó không phải là tin nhắn xác thực thì nó sẽ đóng kết nối.
Hy Lạp

Câu trả lời:


210

Cập nhật 2 lần

Câu trả lời ngắn: Không, chỉ có thể chỉ định trường đường dẫn và giao thức.

Câu trả lời dài hơn:

Không có phương pháp nào trong API WebSockets của JavaScript để chỉ định các tiêu đề bổ sung cho ứng dụng khách / trình duyệt gửi. Đường dẫn HTTP ("GET / xyz") và tiêu đề giao thức ("Sec-WebSocket-Protocol") có thể được chỉ định trong hàm tạo WebSocket.

Tiêu đề Sec-WebSocket-Protocol (đôi khi được mở rộng để sử dụng trong xác thực cụ thể của websocket) được tạo từ đối số thứ hai tùy chọn cho trình tạo WebSocket:

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

Các kết quả trên trong các tiêu đề sau:

Sec-WebSocket-Protocol: protocol

Sec-WebSocket-Protocol: protocol1, protocol2

Một mô hình phổ biến để đạt được xác thực / ủy quyền của WebSocket là triển khai hệ thống bán vé trong đó trang lưu trữ ứng dụng khách WebSocket yêu cầu một vé từ máy chủ và sau đó chuyển vé này trong khi thiết lập kết nối WebSocket trong chuỗi URL / truy vấn, trong trường giao thức, hoặc được yêu cầu như là tin nhắn đầu tiên sau khi kết nối được thiết lập. Sau đó, máy chủ chỉ cho phép kết nối tiếp tục nếu vé hợp lệ (tồn tại, chưa được sử dụng, IP của khách hàng được mã hóa trong các trận đấu vé, dấu thời gian trong vé gần đây, v.v.). Dưới đây là bản tóm tắt thông tin bảo mật WebSocket: https://devcenter.heroku.com/articles/websocket-securance

Xác thực cơ bản trước đây là một tùy chọn nhưng điều này đã không được chấp nhận và các trình duyệt hiện đại không gửi tiêu đề ngay cả khi được chỉ định.

Thông tin xác thực cơ bản (không dùng nữa) :

Tiêu đề ủy quyền được tạo từ trường tên người dùng và mật khẩu (hoặc chỉ tên người dùng) của URI WebSocket:

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

Các kết quả trên trong tiêu đề sau với chuỗi "username: password" base64 được mã hóa:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Tôi đã thử nghiệm auth cơ bản trong Chrome 55 và Firefox 50 và xác minh rằng thông tin xác thực cơ bản thực sự được thương lượng với máy chủ (điều này có thể không hoạt động trong Safari).

Cảm ơn Dmitry Frank cho câu trả lời xác thực cơ bản


38
Tôi đã gặp vấn đề tương tự. Quá tệ là những tiêu chuẩn này được tích hợp rất kém. Bạn mong đợi rằng họ nhìn vào API XHR để tìm các yêu cầu (vì WebSockets và XHR có liên quan) cho API WebSockets, nhưng có vẻ như họ chỉ đang tự phát triển API trên một hòn đảo.
eleotlecram

4
@eleotlecram, tham gia nhóm làm việc HyBi và đề xuất nó. Nhóm này mở cửa cho công chúng và có các công việc đang diễn ra cho các phiên bản tiếp theo của giao thức.
kanaka

5
@Charlie: nếu bạn kiểm soát hoàn toàn máy chủ, đó là một lựa chọn. Cách tiếp cận phổ biến hơn là tạo vé / mã thông báo từ máy chủ HTTP thông thường của bạn và sau đó khách hàng gửi vé / mã thông báo (dưới dạng chuỗi truy vấn trong đường dẫn websocket hoặc dưới dạng tin nhắn websocket đầu tiên). Sau đó, máy chủ websocket xác thực rằng vé / mã thông báo là hợp lệ (chưa hết hạn, chưa được sử dụng, đến từ cùng một IP như khi được tạo, v.v.). Ngoài ra, tôi tin rằng hầu hết các máy khách websockets đều hỗ trợ auth cơ bản (có thể không đủ cho bạn). Thông tin thêm: devcenter.heroku.com/articles/websocket-securance
kanaka

3
Tôi đoán nó theo thiết kế. Tôi có ấn tượng rằng việc triển khai là cố ý mượn từ HTTP nhưng giữ chúng cách nhau càng nhiều càng tốt theo thiết kế. Văn bản trong phần cụ thể tiếp tục: "Tuy nhiên, thiết kế không giới hạn WebSocket đối với HTTP và việc triển khai trong tương lai có thể sử dụng một cái bắt tay đơn giản hơn trên một cổng chuyên dụng mà không cần phát minh lại toàn bộ giao thức. Điểm cuối cùng này rất quan trọng vì các mẫu lưu lượng của tin nhắn tương tác làm. không khớp với lưu lượng HTTP tiêu chuẩn và có thể gây ra tải bất thường trên một số thành phần. "

3
Thật không may, điều này dường như không hoạt động trong Edge. Cảm ơn, MS: /
sibbl

40

Thêm một giải pháp thay thế, nhưng tất cả các trình duyệt hiện đại đều gửi cookie miền cùng với kết nối, do đó, sử dụng:

var authToken = 'R3YKZFKBVi';

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

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

Kết thúc với các tiêu đề kết nối yêu cầu:

Cookie: X-Authorization=R3YKZFKBVi

1
Đây dường như là cách tốt nhất để chuyển mã thông báo truy cập trên kết nối. Tại thời điểm này.
cammil

1
Nếu URI máy chủ WS khác với URI máy khách thì sao?
Đan Mạch

35

Vấn đề tiêu đề ủy quyền HTTP có thể được giải quyết bằng cách sau:

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

Sau đó, tiêu đề HTTP ủy quyền cơ bản phù hợp sẽ được đặt cùng với usernamepassword. Nếu bạn cần Ủy quyền cơ bản, thì bạn đã sẵn sàng.


BearerTuy nhiên, tôi muốn sử dụng và tôi đã sử dụng một mẹo sau: Tôi kết nối với máy chủ như sau:

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

Và khi mã của tôi ở phía máy chủ nhận được tiêu đề Ủy quyền cơ bản với tên người dùng không trống và mật khẩu trống, thì nó sẽ hiểu tên người dùng là mã thông báo.


12
Tôi đang thử giải pháp được đề xuất bởi bạn. Nhưng tôi không thể thấy tiêu đề Ủy quyền được thêm vào yêu cầu của tôi. Tôi đã thử nó bằng các trình duyệt khác nhau, ví dụ Chrome V56, Firefox V51.0 Tôi đang chạy máy chủ của mình trên localhost. vì vậy url websocket là "ws: // myusername: mypassword @ localhost: 8080 / mywebsocket". Bất cứ ý tưởng những gì có thể là sai? Cảm ơn
LearnToLive 11/03/2017

4
Có an toàn để chuyển mã thông qua url không?
Mergasov

2
Tên người dùng trống / bị bỏ qua và mật khẩu không trống vì mã thông báo có thể tốt hơn vì tên người dùng có thể được đăng nhập.
AndreKR

9
Tôi đồng ý với @LearnToLive - Tôi đã sử dụng điều này với wss (ví dụ wss://user:password@myhost.com/ws) và không có Authorizationtiêu đề nào ở phía máy chủ (sử dụng Chrome phiên bản 60)
user9645

6
Tôi có cùng một vấn đề như @LearnToLive và @ user9645; cả chrome và firefox đều không thêm tiêu đề ủy quyền khi URI ở wss://user:pass@hostđịnh dạng. Đây có phải là không được hỗ trợ bởi các trình duyệt, hoặc có điều gì đó không ổn với cái bắt tay?
David Kaczynski

19

Bạn không thể thêm các tiêu đề nhưng, nếu bạn chỉ cần truyền các giá trị cho máy chủ tại thời điểm kết nối, bạn có thể chỉ định một phần chuỗi truy vấn trên url:

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

URL đó hợp lệ nhưng - tất nhiên - bạn sẽ cần sửa đổi mã máy chủ của mình để phân tích cú pháp.


14
cần phải cẩn thận với giải pháp này, chuỗi truy vấn có thể bị chặn, đăng nhập proxy, v.v. vì vậy việc chuyển thông tin nhạy cảm (mã thông báo / mật khẩu / xác thực) theo cách này sẽ không đủ an toàn.
Niết

5
@Nir với WSS, chuỗi truy vấn có thể an toàn
Sebastien Lorber

8
ws là văn bản đơn giản. Sử dụng giao thức ws bất cứ điều gì có thể bị chặn.
Gabriele Carioli

1
@SebastienLorber không an toàn khi sử dụng chuỗi truy vấn, nó không được mã hóa giống như áp dụng cho HTTPS, nhưng vì giao thức "ws: // ..." được sử dụng, nên thực sự không có vấn đề gì.
Lu4

5
@ Lu4 chuỗi truy vấn được mã hóa, nhưng có một số lý do khác để không thêm dữ liệu nhạy cảm như tham số truy vấn URL stackoverflow.com/questions/499591/are-https-urls-encrypted/ tựa & blog.httpwatch.com/2009 /

16

Bạn không thể gửi tiêu đề tùy chỉnh khi bạn muốn thiết lập kết nối WebSockets bằng API WebSockets của JavaScript. Bạn có thể sử dụng Subprotocolscác tiêu đề bằng cách sử dụng hàm tạo lớp WebSocket thứ hai:

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

và sau đó bạn có thể lấy các tiêu đề Subprot Protocol bằng Sec-WebSocket-Protocolkhóa trên máy chủ.

Ngoài ra còn có một hạn chế, các giá trị tiêu đề Subprot Protocol của bạn không thể chứa dấu phẩy ( ,)!


1
Một Jwt có thể chứa một dấu phẩy?
Cgill

1
Tôi không tin như vậy. JWT bao gồm ba tải trọng được mã hóa cơ sở64, mỗi tải được phân tách bằng một khoảng thời gian. Tôi tin rằng quy tắc này ra khả năng của một dấu phẩy.
BillyBBone

2
Tôi đã thực hiện điều này và nó hoạt động - chỉ cảm thấy kỳ lạ. cảm ơn
BatteryAcid

3
bạn có gợi ý chúng tôi sử dụng Sec-WebSocket-Protocoltiêu đề thay thế cho Authorizationtiêu đề không?
rrw

13

Gửi tiêu đề ủy quyền là không thể.

Đính kèm một tham số truy vấn mã thông báo là một tùy chọn. Tuy nhiên, trong một số trường hợp, có thể không mong muốn gửi mã thông báo đăng nhập chính của bạn dưới dạng văn bản đơn giản dưới dạng tham số truy vấn vì nó mờ hơn so với sử dụng tiêu đề và cuối cùng sẽ được ghi lại ở mọi nơi. Nếu điều này làm tăng mối lo ngại về bảo mật cho bạn, một giải pháp thay thế là sử dụng mã thông báo JWT thứ cấp chỉ cho công cụ ổ cắm web .

Tạo điểm cuối REST để tạo JWT này , tất nhiên chỉ có thể được truy cập bởi người dùng được xác thực bằng mã thông báo đăng nhập chính của bạn (được truyền qua tiêu đề). Ổ cắm web JWT có thể được định cấu hình khác với mã thông báo đăng nhập của bạn, ví dụ: thời gian chờ ngắn hơn, vì vậy việc gửi xung quanh dưới dạng tham số yêu cầu nâng cấp của bạn sẽ an toàn hơn.

Tạo một JwtAuthHandler riêng cho cùng một tuyến đường mà bạn đăng ký SockJS eventbusHandler trên . Đảm bảo trình xử lý xác thực của bạn được đăng ký trước, vì vậy bạn có thể kiểm tra mã thông báo ổ cắm web dựa trên cơ sở dữ liệu của mình (JWT phải được liên kết với người dùng của bạn trong phần phụ trợ).


Đây là giải pháp an toàn duy nhất tôi có thể đưa ra cho các websockets API Gateway. Slack làm điều gì đó tương tự với API RTM của họ và họ có thời gian chờ 30 giây.
andrhamm

2

Hoàn toàn hack nó như thế này, nhờ câu trả lời của kanaka.

Khách hàng:

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

Máy chủ (sử dụng Koa2 trong ví dụ này, nhưng phải giống nhau ở mọi nơi):

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
Không phải điều này chỉ chuyển mã thông báo của bạn trong phần mà khách hàng của bạn có nghĩa vụ yêu cầu một hoặc nhiều giao thức cụ thể? Tôi có thể làm việc này, không có vấn đề gì, nhưng tôi quyết định không làm điều này và thay vào đó làm những gì Motes đề xuất và chặn cho đến khi mã thông báo xác thực được gửi trên onOpen (). Quá tải tiêu đề yêu cầu giao thức có vẻ sai đối với tôi và vì API của tôi dành cho tiêu dùng công cộng, tôi nghĩ rằng nó sẽ gây nhầm lẫn cho người tiêu dùng API của tôi.
Jay

0

Trường hợp của tôi

  • Tôi muốn kết nối với máy chủ WS sản xuất www.mycompany.com/api/ws...
  • sử dụng thông tin xác thực (cookie phiên) ...
  • từ một trang địa phương ( localhost:8000).

Cài đặt document.cookie = "sessionid=foobar;path=/"sẽ không giúp vì tên miền không khớp.

Giải pháp :

Thêm 127.0.0.1 wsdev.company.comvào /etc/hosts.

Bằng cách này, trình duyệt của bạn sẽ sử dụng cookie từ mycompany.comkhi kết nối với www.mycompany.com/api/wskhi bạn đang kết nối từ một tên miền phụ hợp lệ wsdev.company.com.


-1

Trong tình huống của tôi (Thông tin chi tiết về chuỗi thời gian Azure wss: //)

Sử dụng trình bao bọc ReconnectingWebsocket và có thể đạt được việc thêm các tiêu đề bằng một giải pháp đơn giản:

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

Trường hợp tải trọng trong trường hợp này là:

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

Về mặt kỹ thuật, bạn sẽ gửi các tiêu đề này thông qua chức năng kết nối trước giai đoạn nâng cấp giao thức. Điều này làm việc cho tôi trong một nodejsdự án:

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

3
Điều này là cho máy khách websocket trong npm (cho nút). npmjs.com/package/websocket Nhìn chung đây sẽ là chính xác những gì tôi đang tìm kiếm, nhưng trong trình duyệt.
arnuschky

1
Nó bị hạ cấp vì tham số tiêu đề này trong lớp giao thức WebSocket và câu hỏi là về các tiêu đề HTTP.
Toilal

" headersphải là null hoặc một đối tượng chỉ định các tiêu đề yêu cầu HTTP tùy ý bổ sung để gửi cùng với yêu cầu." từ WebSocketClient.md ; do đó, headersđây là lớp HTTP.
mẹ

Ngoài ra, bất kỳ ai muốn cung cấp các tiêu đề tùy chỉnh nên ghi nhớ chữ ký hàm của connectphương thức, được mô tả là connect(requestUrl, requestedProtocols, [[[origin], headers], requestOptions]), ví dụ, headersnên được cung cấp cùng với requestOptions, ví dụ , ws.connect(url, '', headers, null). Chỉ origincó thể bỏ qua chuỗi trong trường hợp này.
mẹ

-4

Bạn có thể chuyển các tiêu đề dưới dạng khóa-giá trị trong tham số thứ ba (tùy chọn) bên trong một đối tượng. Ví dụ với mã thông báo ủy quyền. Còn lại giao thức (tham số thứ hai) là null

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

Chỉnh sửa: Có vẻ như cách tiếp cận này chỉ hoạt động với thư viện nodejs chứ không phải với triển khai trình duyệt tiêu chuẩn. Rời khỏi nó bởi vì nó có thể hữu ích cho một số người.


Có hy vọng của tôi trong một giây. Dường như không có sự quá tải khi sử dụng thông số thứ 3 trong WebSocket ctor.
Levitikon

Có ý tưởng từ mã wscat. github.com/websockets/wscat/blob/master/bin/wscat dòng 261 sử dụng gói ws. Nghĩ rằng đây là một cách sử dụng tiêu chuẩn.
Gật đầu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.