Problem in communicating with Binance exchange in NodeJS web socket - javascript

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.

Related

MQTT TLS connection

I would like to connect a test MQTT-Client to my Node.js application as a MQTT-Broker. I am using the aedes library from moscajs
My MQTT-Client is the tool "MQTT-Explorer" and here is my Node.js application:
const fs = require('fs');
const aedes = require('aedes')();
const options = {
key: fs.readFileSync('certs/server_key.pem'),
cert: fs.readFileSync('certs/server_cert.pem'),
};
// const server = require('net').createServer(aedes.handle);
const server = require('tls').createServer(options, aedes.handle);
const PORT = 8881;
server.listen(PORT, () => {
console.log(`server is up and running: PORT [${PORT}] - ID [${aedes.id}]`);
});
I can connect without any problems to PORT=1881 with const server = require('net').createServer(aedes.handle) and I also can connect to PORT=8881 with const server = require('tls').createServer(options, aedes.handle)
With the Tool xca-2.4.0.msi XCA 2.4.0 I have created a ca.pem CERTIFICATE File and a CERTIFICATE server_cert.pem and a server_key.pem PRIVATE KEY (signed from ca.pem) as a Server. The key for CA and the Server are different:
For my MQTT-Client, under ADVANCED, CERTIFICATES, SERVER CERTIFICAT (CA) I selected the ca.pem File. If I select "Encryption", it works. But if select "validate certificate", error: Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the certs list
Unfortunately I don't know what I'm doing wrong, thanks in advance :(
MQTT Explorer is built using Node.js and the MQTT library MQTT.js. As per this issue:
Node.js requires the IP address to be in the subjectAltNames for the cert and not in the CN. Maybe MQTT.fx isn't requiring that, but it should.
and:
If your server's certificate says CN=localhost in the Subject field, connect using localhost and not 127.0.0.1 and it should work. If it says CN=127.0.0.1, you have to make a new one because Node.js won't validate the IP address unless it's in the SAN extension. There is a way to work around it in code (I think it's an option called checkServerIdentity), but I would prefer to fix my certificate if I had this problem.
A rationale for the approach taken in Node is set out in this answer which includes the following quote from RFC2818: HTTP Over TLS
:
In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI.
As you are using MQTT over TLS (as opposed to HTTP Over TLS) you could argue that the above does not apply but, given that the main use of the TLS library is for HTTP traffic, it makes sense that it confirms to the RFC by default.
You have a few options including:
Use a hostname (e.g. localhost) rather then an IP when creating the certificate/connecting.
Add the IP as a subjectAltName
Modify the library to use a noop checkServerIdentity (see this answer).
Use another application for testing (not really recommended as some applications will work and others will not). The issue quoted above mentions that MQTT.fx works.

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

Restrict PHP API for specific domains which are saved in my database

I have created an API which takes the hostkey or API_KEY and then it validates and gives back JWT token. Everything is working fine, I can't access the restricted routes without Hostkey.
ISSUE
The major issue is that what will happen if someone gives this hostkey to others as it will no longer be protected or it will be misused. So what I want to do is not only validate the hostkey but also validate the domain from which request came from. It is kind of paid service and I really want to restrict is to specific domains. Just like google does with MAP Api as if we add that map key to other domain it throws an error.
The only way to do this is to check the origin and referrer headers.
Unfortunately, server to server this can't be done reliably as the referrer and origin headers would be set by the coder and so can be spoofed easily. For server to server calls you would be better off whitelisting IP addresses that are allowed to make calls to your APIS. In this case use something like How to get Real IP from Visitor? to get the real IP of the server and verify it against whitelisted IPs.
Assuming this is a JS call in browser and not server to server, and that you trust the browser, the only way this can really be done is by verifying the referrer and origin headers. This can still be spoofed with a browser plugin or even with a tool like Postman so I don't recommend it for high security. Here is a PHP example for verifying the origin or referrer.
$origin_url = $_SERVER['HTTP_ORIGIN'] ?? $_SERVER['HTTP_REFERER'];
$allowed_origins = ['example.com', 'gagh.biz']; // replace with query for domains.
$request_host = parse_url($origin_url, PHP_URL_HOST);
$host_domain = implode('.', array_slice(explode('.', $request_host), -2));
if (! in_array($host_domain, $allowed_origins, false)) {
header('HTTP/1.0 403 Forbidden');
die('You are not allowed to access this.');
}
Optionally also CORS headers are good as commented by #ADyson Cross-Origin Request Headers(CORS) with PHP headers
I would like to suggest making a quote or limit for the number of request, so when the paid API reach for 100 request the key will stop working, then the person who paid will not give the key for others. This is not perfect solution, but I would suggest it cause most API services uses it.

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.

Making cross-domain requests while maintaining original IP address

My first attempt was to make a reverse proxy in Express which would allow me to request to localhost:3000/request and have it forward to somesite.com/request.
My code was:
var request = require('request');
app.get('/', function(req,res) {
var newurl = 'http://google.com/';
request(newurl).pipe(res);
});
This issue with this, is it changes the IP address of the request, to my server's IP.
My next attempt was JSONP, even though this is an HTML webpage, not a JSON file.
var tag = document.createElement("script");
tag.src = 'http://example.com/search?q=test';
document.getElementsByTagName("head")[0].appendChild(tag);
The error for that was: Uncaught SyntaxError: Unexpected token <
You can certainly get around CORS with a server-side proxy. You can use a pre-built module like node-http-proxy to do that and to handle most of the implementation for you.
However, the target destination site will see the request coming from the IP address of your proxy server and you cannot change that.
A proper proxy will insert headers into the original HTTP request that contains the original browser's IP address and the receiving server could look at those additional headers if it chose to. But, if it only looks at the IP address that the request actually comes from, then it will see the IP address of your proxy server and you can't change that. For data to flow from your proxy to the target destination server and then the request to come back through your proxy and back to the original browser, there will necessarily be a TCP connection between your server and the target destination and the source IP address has to be your server (that's the only way the data can flow back to your server so you can then forward it back to the browser).
As for JSONP, JSONP requires cooperative support from the target server (the requested data has to be sent back in a piece of Javascript that when run will call the desired callback function). If that server doesn't explicitly support JSONP, then you can't use it.

Categories

Resources