WebSockets and HTTP headers - javascript

I am trying to figure out how to add a header with an authorization token to a WebSocket connection, after it has been established. Basically, to use a cookie in order to validate each message in the connection.
There is a great discussion at HTTP headers in Websockets client API already, yet it did not really help me.
One of the the suggestions there was to do:
document.cookie = 'X-Authorization=' + token + '; path=/';
Unfortunately, it did not help me.
func handler(ws *websocket.Conn, h *Hub) {
go h.run()
h.addClientChan <- ws
for {
config := ws.Config()
fmt.Println("Headers length:", len(config.Header))
...
Shows a map with 0 elements before and after I add the cookie.
Is it possible to alter WebSocket connection headers, after the connection has been established?
Do I even need to add an authorization token to a WebSocket connection, provided I authenticate each time, or is it only useful to re-establish a connection and just a useless check after the fact?

Is it possible to alter WebSocket connection headers, after the
connection has been established?
You can't set a cookie upon receipt of a WebSocket message. Once the WebSocket connection has been established, it's an open TCP socket and the protocol is no longer http, thus there is no built-in way to exchange cookies.
You can use authorizathion on the first http request, where both sides establish protocol for exchange data.

Related

Is it dangerous to establish WebSocket connection in a js file?

So, basically, I have this code :
let socket = new WebSocket('ws://localhost:8080/server.php');
socket.onopen = () => {
console.log('connection established !');
}
I use this code to establish a connection for a Real-Time Quiz. But after going to the Sources page of my inspector, I can see the whole javascript code in my browser, including ws://localhost:8080/server.php. Is it dangerous to show it (unintentionally) ? If someones creates a script and puts in it the same url (not localhost, it's just an example), can he receive/send data to the server ?
yes,it is dangerous. u can:
verify the client http request header for example 'Origin'. make sure the client website is the right client website.
use a TSL websocket service, visit to the server over SSL. So the protocol is changing to: wss://
give the client a request token, put this token in header or in post data, the server verify this token.
check the request times of a client in limited time. make sure a specific client won't request too frequently

Connect client socket to a cookie

I am making a chat program.
I am using an Nginx server and NodeJS.
I have setup a websocket via ssl and that works fine.
I have decided to use cookies for authentication.
There are two functions which are crucial:
mconnection.prototype.make_server_https=function(){
console.log('Make server https');
var cthis=this;
var server_https=modules.https.createServer({
key: this.ssl_key,
cert:this.ssl_cert,
ca:this.ssl_ca
},(request,response)=>{
console.log('### CreateServer ###');
console.log('CreateServer, Request:');
console.log(request);
console.log('CreateServer, Response:');
console.log(response);
console.log('######');
and
mconnection.prototype.make_server_websocket=function(){
var server_websocket=new modules.ws.Server({server:this.server_https});
var cookie = require("cookie");
var cthis=this;
//whenever a new client connects with the server.
server_websocket.on('connection', function(client_socket, request){
console.log('### On Connection ###');
console.log('OnConnection, Client Socket:');
console.log(client_socket);
console.log('OnConnection, Request:');
console.log(request);
console.log('######');
If I do state the port number in the client url,function make_server_https gets run and inside there i can access the cookie and set it via the response object.
but in the original url,function make_server_websocket gets run, and there i have access to the client_socket on the server. But there it seems i dont have access to the cookies.
I need to client_websocket to start the connection with this given client. And I need to tie it somehow with the cookies login information.
But i never have both at the same time so i dont get how i could connect them to make the login happen.
I am probably misunderstanding something, any help in the right direction would really be appreciated.
you have to serve you index page from node server using GET then when the request reaches backend you will have response object which can then be used to SET-COOKIE if not set from backend.
And after GET request is complete COOKIE will be added in browser, when next request is made for websocket connection COOKIE will be added to the request in REQUEST HEADERS by the browser which will be available in backend through request object.
And if you decide to use it in login system then you can SET-COOKIE on successfull login.
i got it. its an event called on headers, not on connection. and there i can just push onto the headers.

Delays in HTTP requests via Node.js compared to browser

In using Node.js to query some public APIs via HTTP requests. Therefore, I'm using the request module. I'm measuring the response time within my application, and see that my application return the results from API queries about 2-3 times slower than "direct" requests via curl or in the browser. Also, I noticed that connections to HTTPS enabled services usually take longer than plain HTTP ones, but this can be a coincidence.
I tried to optimize my request options, but to no avail. For example, I query
https://www.linkedin.com/countserv/count/share?url=http%3A%2F%2Fwww.google.com%2F&lang=en_US
I'm using request.defaults to set the overall defaults for all requests:
var baseRequest = request.defaults({
pool: {maxSockets: Infinity},
jar: true,
json: true,
timeout: 5000,
gzip: true,
headers: {
'Content-Type': 'application/json'
}
});
The actual request are done via
...
var start = new Date().getTime();
var options = {
url: 'https://www.linkedin.com/countserv/count/share?url=http%3A%2F%2Fwww.google.com%2F&lang=en_US',
method: 'GET'
};
baseRequest(options, function(error, response, body) {
if (error) {
console.log(error);
} else {
console.log((new Date().getTime()-start) + ": " + response.statusCode);
}
});
Does anybody see optimization potential? Am I doing something completely wrong? Thanks in advance for any advice!
There are several potential issues you'll need to address given what I understand from your architecture. In no particular order they are:
Using request will always be slower than using http directly since as the wise man once said: "abstraction costs". ;) In fact, to squeeze out every possible ounce of performance, I'd handle all HTTP requests using node's net module directly. For HTTPS, it's not worth rewriting the https module. And for the record, HTTPS will always be slower than HTTP by definition due to both the need to handshake cryptographic keys and do the crypt/decrypt work on the payload.
If your requirements include retrieving more than one resource from any single server, assure that those requests are made in order with the http KeepAlive set so you can benefit from the already open socket. The time it takes to handshake a new TCP socket is huge compared to making a request on an already open socket.
assure that http connection pooling is disabled (see Nodejs Max Socket Pooling Settings)
assure that your operating system and shell is not limiting the number of available sockets. See How many socket connections possible? for hints.
if you're using linux, check Increasing the maximum number of tcp/ip connections in linux and I'd also strongly recommend fine tuning the kernel socket buffers.
I'll add more suggestions as they occur to me.
Update
More on the topic of multiple requests to the same endpoint:
If you need to retrieve a number of resources from the same endpoint, it would be useful to segment your requests to specific workers that maintain open connections to that endpoint. In that way, you can be assured that you can get the requested resource as quickly as possible without the overhead of the initial TCP handshake.
TCP handshake is a three-stage process.
Step one: client sends a SYN packet to the remote server.
Step two: the remote server replies to the client with a SYN+ACK.
Step three: the client replies to the remote server with an ACK.
Depending on the client's latency to the remote server, this can add up to (as William Proxmire once said) "real money", or in this case, delay.
From my desktop, the current latency (round-trip time measure by ping) for a 2K octet packet to www.google.com is anywhere between 37 and 227ms.
So assuming that we can rely on a round-trip mean of 95ms (over a perfect connection), the time for the initial TCP handshake would be around 130ms or SYN(45ms) + SYN+ACK(45ms) + ACK(45ms) and this is a tenth of a second just to establish the initial connection.
If the connection requires retransmission, it could take much longer.
And this is assuming you retrieve a single resource over a new TCP connection.
To ameliorate this, I'd have your workers keep a pool of open connections to "known" destinations which they would then advertise back to the supervisor process so it could direct requests to the least loaded server with a "live" connection to the target server.
Actually, I have some new elements good enough to open a real answer. Having a look at the way request uses the HTTP agent can you please try the following :
var baseRequest = request.defaults({
pool: false,
agent: false,
jar: true,
json: true,
timeout: 5000,
gzip: true,
headers: {
'Content-Type': 'application/json'
}
});
This will disable connection pooling and should make it a lot faster.

websocket reconnect using session id

I'm using Java and Websocket API for my application
This is the problem I face :
I open a websocket connection to connect through certain server :
ws = new WebSocket(targetURL);
When I send every message using ws, I always check the state of the ws first, by :
if(ws.readyState == 1) {
ws.send(request);
} else {
alert("THE CONNECTION IS CLOSED. PLEASE REFRESH YOUR BROWSER.");
}
I saved an information in my EndPoint during the communication between client and server. But sometimes, when I want to send message from client, the state is not 1, which means that the ws is no longer open (CMIIW). If I try to connect again using ws = new WebSocket(targetURL), it will create a new connection, thus the information in my EndPoint become lost.
So my question is :
Is there any way to reconnect a closed connection of Websocket using Session ID or any other unique ID so that the information in my EndPoint can be preserved?
Thank you
WebSockets use a HTTP handshake, and it will send whatever cookie they have for that origin. So a way of doing it, is assign a cookie to you clients, and they will forward it on connection. You could even set the cookie in the handshake response, if the framework you are using it allows it. You can use that cookie on connection to figure out the ID or assign a new ID if no cookie.
Other option is to create for example your own "hello" protocol, where the browser has to send a special command before start, indicating its ID if any, or just an null ID if it is first time.
But aside of the cookie, there is not built-in mechanism for that.

How to read status code from rejected WebSocket opening handshake with JavaScript?

I created a WebSocket client in JavaScript
if ("WebSocket" in window) {
ws = new WebSocket(url);
ws.binaryType = "arraybuffer";
} else if ("MozWebSocket" in window) {
ws = new MozWebSocket(url);
ws.binaryType = "arraybuffer";
}
and a WebSocket server application. For certain cases I programmed the server to reject the connection request and provide an error code.
In e.g. Firefox Console then a message is shown
Firefox can't establish a connection to the server at ws://123.123.123.123:1234/.
and it provides the status code
HTTP/1.1 403
which is the error code that I have sent by my WebSocket server.
My question is: how can I read this status code in my JavaScript client?
ws.onerror = function(e) {
console.log(e);
};
ws.onclose = function(e) {
console.log(e);
};
are both called, but none of the Event objects contains this error code.
The spec forbids reading the HTTP status code (or anything like it) from the WebSocket object because otherwise the WebSocket object could be used to probe non-WebSocket endpoints, which would be a security issue:
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:
A server whose host name could not be resolved.
A server to which packets could not successfully be routed.
A server that refused the connection on the specified port.
A server that failed to correctly perform a TLS handshake (e.g., the server certificate can't be verified).
A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).
A WebSocket server that sent a correct opening handshake, but that specified options that caused the client to drop the connection (e.g. the server specified a subprotocol that the client did not offer).
A WebSocket server that abruptly closed the connection after successfully completing the opening handshake.
— https://www.w3.org/TR/websockets/#feedback-from-the-protocol
There is another way to do it though!
The WebSocket protocol allows for custom close codes:
4000-4999
Status codes in the range 4000-4999 are reserved for private use and thus can't be registered. Such codes can be used by prior agreements between WebSocket applications. The interpretation of these codes is undefined by this protocol.
— https://www.rfc-editor.org/rfc/rfc6455#section-7.4.2
In your server-side logic, even when you ultimately want to reject the connection (like say the user is currently unauthenticated), do this instead:
Accept the WebSocket connection
Immediately close the connection with a custom close status
The client can now look at the CloseEvent.code to know why the connection was rejected.
You don't need to do this every time the server wants to reject a WebSocket connection. For example, I'd still reject the connection with a 4xx HTTP status if the request isn't a proper WebSocket request, or for security reasons (like if the anti-CSWSH Origin check fails). You only need to use the WebSocket close status for cases that you want the client-side logic to handle.

Categories