Client Websocket SSL -- Connect to specific IP address - javascript

Let's say there is a website called https://example.com. A DNS lookup on the domain returns the following A records:
1.1.1.1 A example.com
1.1.1.2 A example.com
1.1.1.3 A example.com
1.1.1.4 A example.com
Each one of these addresses has the SSL certificate for example.com and is running a websocket server on port 443.
A client can connect to one of the servers using something like
var wss = new WebSocket("wss://example.com");
This will connect the client to a server based on DNS Round Robin: the client has no control over what server she connects to.
My question is the following:
How can a client connect to a specific address without having certificate issues?
For example, if the client knows that she wishes to connect to 1.1.1.4 and tries to connect with
var wss = new WebSocket("wss://1.1.1.4");
she will encounter an ssl error since the server cannot have the individual address as an alternative name in its certificate.
Is it possible to specify the hostname along with an individual address when making the connection; something like
var wss = new WebSocket("wss://example.com?ip=1.1.1.4");
I believe you can do this using CURL with something like curl https:// DOMAIN.TLD --resolve 'DOMAIN.TLD:443:IP_ADDRESS'

To avoid the certificate issue, request a domain name instead of an IP. I suggest this :
1.1.1.1 A example.com
1.1.1.2 A example.com
1.1.1.3 A example.com
1.1.1.4 A example.com
1.1.1.1 A a.example.com
1.1.1.2 A b.example.com
1.1.1.3 A c.example.com
1.1.1.4 A d.example.com
and one single SAN certificate for all of the 5 domains (example.com + [a-d].example.com)
You can test any of [a-d].example.com individually, or example.com, all will respond with the same certificate and there's no issue I think.

Related

Problem in communicating with Binance exchange in NodeJS web socket

When I execute the following piece of code in the app.js file
const WebSocket = require('ws');
const ws = new WebSocket('wss://stream.binance.com:9443/ws/!miniTicker#arr');
ws.on('message', function (data){
//const result = JSON.parse(data);
console.log(data);
});
I get the following error and I cannot receive data.
10.10.34.34 (what your DNS server resolved stream.binance.com to) is a local IP address! You are not even reaching Binance. It's probably a captive portal or some firewall.
Open that IP address in the browser and check whether you get some sort of web interface that gives you a clue about what is blocking your request. You can also try changing your DNS server to Google (8.8.8.8) or some other public DNS server instead of relying on your router.
Update:
In your comment you mentioned your DNS server is 178.22.122.100 (i.e., shecan.ir). If I try to resolve the domain with that server, I also get nonsensical results (10.10.34.34 and d0::11). According to this book, this is part of Iranian censorship, and your ISP would handle this IP address to show a page about this domain being blocked if you'd open it in a browser.
So, an alternative public DNS server without censorship such as 8.8.8.8, 1.1.1.1 or 9.9.9.9 should help. If this doesn't work then those DNS servers are also blocked by your ISP and you would need to use a VPN or other means of bypassing government censorship.

How to downgrade socket.io websocket to WS from WSS?

I am making a website that accesses the devices sensors and sends them via socket.io to my local machine.
I can't access the device sensors without HTTPS, so I have to use HTTPS for the website, which is why I uploaded my site to Heroku. The problem is the localhost server I open on my computer is HTTP, and my HTTPS website can't send data from HTTPS (heroku site) to HTTP (local machine: localhost). Is there any way I can share the data between them?
This is the code used to connect to localhost written on the heroku client side site:
const socket = io("https://192.168.1.15:16", { transports: ['websocket', 'polling', 'flashsocket']});
While this is what I use in my local machine:
const httpServer = require("http").createServer(app);
const io = require("socket.io")(httpsServer,{
});
as per socket.io documentation
I get this error:
Mixed Content: The page at '**The website**' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://192.168.1.35:16/socket.io/?EIO=4&transport=websocket'. This request has been blocked; this endpoint must be available over WSS.
Like #R3FL3CT says, it's most likely a CORS issue - check your console to be sure.
It seems that the initial request that it makes is the one getting blocked. For example,
const socket = io('wss://echo.websocket.org/');
socket.on("connection", (socket) => {
console.log(`Connected!`)
});
Would get blocked with the error
Access to XMLHttpRequest at 'https://echo.websocket.org/socket.io/?EIO=4&transport=polling&t=Nb17pKo' from origin 'http://127.0.0.1:5501' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Whereas just using a pure WebSocket
const websocket = new WebSocket('wss://echo.websocket.org/');
websocket.onopen = e => {
console.log(`Connected!`);
}
Connected okay.
So your solution is to either roll back to an earlier version of socket.io that doesn't force cors (before v3), or just use your own WebSocket - example: https://www.websocket.org/echo.html
Here's one way you could do something to try and communicate. If you host your own version of CORS Anywhere, you can communicate with http:// websites. I have a working link you can use, if you don't want to have to host one, but here the Github is. The way it works, is that you append the URL to the end of your URL for CORS Anywhere.
e.g https://cors.anywhere.com/google.com

Getting server certificates from https/ws server automatically

I'm hosting a website on git pages that opens a video stream on certain clicks over a secure websocket (wss) using this library: github.com/websockets/ws. When connecting video from an http page over a simple websocket (ws) everything was fine, but I realized the github page will be hosted over https and firefox does not allow unsecured websocket connections over https, wss is required.
To do that I followed the steps on the websockets ws page and the websocket server is sort of "hiding" behind an https server that does the certificate handshake. The problem is this: If I use my web page normally and open the link that starts a wss connection to the video it doesn't work, the server doesn't know it exists. If I first visit the server directly via IP and port using an https://IP:999 link it retrieves the certs and from then on the website works indefinitely.
Is there a way to do this naturally that doesn't require me trying to visit a headless server to do the cert handshake? I just want to open a wss connection but the overhead of having to visit a server directly for certs seems a bit bizarre.
The server setup looks like this:
const server = https.createServer({
cert: fs.readFileSync('./cert/cert.pem'),
key: fs.readFileSync('./cert/key.pem'),
}, function (req, res) {
console.log(new Date() + ' ' +
req.connection.remoteAddress + ' ' +
req.method + ' ' + req.url);
res.writeHead(200);
res.end("hello foobarbackend\n");
});
this.wsServer = new ws.Server({
server
})
this.wsServer.on("connection", (socket, request) => {
return this.onSocketConnect(socket, request)
})
server.listen(9999, '0.0.0.0');
Once the certs are retrieved via https://:9999 I can then play videos no problem on that browser at wss://:9999, I must be missing something.
The question here is answered entirely by the world of SSL/TLS. The issue here was that secure browsers will pretty much silently reject WSS:// connections to servers with self signed certificates. My certificates were self signed.
This is why the user would have to navigate to the server IP directly over HTTPs first and then accept the warnings. From there it's business as usual.
What needed to be done was register a domain name for the IP the server was located on (Droplet). Then certbot was used to generate real certificates (key, cert) for the domain. I replaced the cert.pem and key.pem above with the true generated ones. Domain name can be anything like mywebsitewhatever.app.
Now on the client side you can open up a connection to wss://mywebsitewhatever.app:9999 and the browser will accept it automatically and things work. No warnings or navigation to a warning page to accept.

Only allow computers on the same network using Express-ip-filter

So I'm using localtunnel to expose my ports over the internet, but I only want to let devices on the same network as the server access the server.
I'm using express-ip-filter to filter away anything that's on a different network. I tried a few things: first I tried using 192.168.1.0/24 as the only ips that could access the website, but that didn't work, as it didn't let anything in. I then tried using the ip I got from WhatsMyIp, but that wouldn't let any device in. I then found out that express-ip-filter spits out a message saying that a certain ip was not allowed and, on every device, independently on the network it was connected to, the address was 127.0.0.1. I tried confirming by only allowing 127.0.0.1, and then every device could access the server. Why would ip-filter only get 127.0.0.1 as ip? Here's my code as a reference:
// Init dependencies
var express = require('express'),
ipfilter = require('express-ipfilter').IpFilter
app = express()
// Blacklist the following IPs
var ips = ['192.168.1.0/24']
// Create the server
app.use(ipfilter(ips, { mode: "allow" }))
app.get('/', function (req, res) {
res.send('Hi')
})
app.listen(8080, () => console.log('Up'))
From my limited understanding of localtunnel it seems like it proxies users requests to you via the localtunnel software which causes all users to have the same IP. In laymans terms:
User connects to your site through localtunnel
localtunnel copies the users request and sends it to your computer
Your application receives the request but it looks like all traffic is coming from localtunnel because it's incredibly difficult if not impossible for localtunnel to imitate someone else's IP.
Why use localtunnel at all if you only want devices on the same network to connect, you don't need to do any port forwarding or DNS setup if you just want to access another machine on the same local network.
If you really do need to tunnel connections then there is a solution, not with localtunnel(Which as far as i can tell does not use forwading headers, although if someone knows if they do ill change my answer) but using https://ngrok.com instead which does exactly the same thing but also sends a little extra bit of data in every request which tells the application what the clients actual IP is.
Install ngrok
Run ngrok http -subdomain=(the subdomain you want) 80
Edit your application code to find the real client IP
var findProxyIP = function(req) {
var realIP = req.header('x-forwarded-for');
return realIP;
}
app.use(ipfilter(ips, {
mode: "allow",
detectIP: findProxyIP
}));
ngrok is much more complex and has a lot more features compared to localtunnel, however, it is freemium software and its free plan is quite limiting.

How does Electron proxy URLs?

I created a small server which, for now, just outputs the request.url:
const http = require('http');
http.createServer(onRequest).listen(8080);
function onRequest(clientRequest, clientResponse) {
console.log(clientRequest.url);
}
Using the Electron APIs we can set up a proxy: to proxy all the urls through this server.
So, I'm running my server on localhost:8080 and use the --proxy-server http://localhost:8080 to redirect the traffic through my proxy server. This allows me to change some snippets in the HTML and only then render it in Electron.
When I access http://ionicabizau.net the request.url on the server side is http://ionicabizau.net.
How come that we can override the request url in such a way? What does Electron in the background?
First I thought that it just has to do with appending it like this:
http://localhost:8080/http://ionicabizau.net
But actually, that arrives on the server like /http://ionicabizau.net (notice the first slash).
What's the magic that Electron does to change the url of the request object?
When Electron (or anything else) makes an HTTP request, it connects to the target server and port and sends a message like the following:
GET / HTTP/1.1
Host: www.example.com
Most servers interpret this as an HTTP request for the full URL http://www.example.com/. When you specify a proxy server, that affects what server the HTTP client connects to, but it doesn't change the content of the request (so the requested URL is still http://www.example.com/).
So there's really nothing special that Electron needs to do to "override" the request URL... any HTTP client specifies the request URL as part of the message it sends to the server, and this is independent of which server that message is sent to.

Categories

Resources