Is directory traversal via request.url possible? - node.js

I'm coming from PHP, where you can inject double dots into a URL to try directory traversal. In NodeJS, it seems that you get the Http webserver automatically removes the double dots from the URL.
For example, if you go to http://example.com/static/../app.js, it seems like Node redirects to http://example.com/app.js, which then throws a 404 in my case, because there is no callback for URLs not starting with /static/.
Is it safe to assume that directory traversal via request.url is not possible in a NodeJS HTTP webserver created using the http package?

I was gonna say that you can be sure that it's not possible, then I tried and I have to say that no, it doesn't seem like the http module removes '/../'. The redirection you saw is done in the browser. So whether it's a security risk or not depends on how your static handler is implemented.
Proof of concept:
// Server
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(req.url);
}).listen(1337);
Curl it:
curl --path-as-is "http://localhost:1337/static/../app.js"
# /static/../app.js
So if you use a homebuilt static handler that just uses path.resolve() you're screwed. Hopefully popular ones like express.static have thought about this, but i haven't tried it.
Update
Express indeed responds with a 404 "Error: Forbidden".

Related

Https createServer, load cookie and load clients index.html

I am trying to setup a login system on a website.
In order to do that, I have to load http only cookies.
In order to load them, I have to send them back to the client via the response object in the createServer function when https starts up.
I have successfully done that via here:
setting up https cookie in nodejs
The problem is twofold.
The https server cookie only loads if I include the port number in the url.
How do I get it to load without including the port number?
When the server kicks in and loads the cookie, the static index.html that was always supposed to load on the client, doesn't load and instead all i get is what was written into the response object on the server. How do I get to load the cookie, and just load that static html file?
I have tried sending in the entire html file as a respnose from the server side. But I'm not sure about that, plus i get MIME type problems in the browser.
I am not sure for the first part but for 2nd one,
you have to properly mention about response data type in response header.
so your should be something like this one:
var app = express(); app.get('/test', function(req, res) { res.sendFile('views/test.html', {root:__dirname}) });
For your first part of the question "How do I get it to load without including the port number?" You can try creating virtual host e.g for localhost:3000 will be something.xyz.
And for your second part you need to serve index.html with render method as follow
server.get('/', function (req, res) {
res.render('index', { greeting: 'Welcome' });
});
Where index is you static file inside view directory.
I've created a small demo, that might get you on the right track:
https://github.com/bergur/simple-server-with-websocket
This example includes:
https web server
websocket server
logic to get the cookies
serving a temp.html file without express
example of javascript class
example of dependency injection in nodejs

What does http and https module do in Node?

Can someone help me in understanding what does http and https module do in Express?
I was going through the following docs on w3schools
From definition it says
Node.js has a built-in module called HTTP, which allows Node.js to
transfer data over the Hyper Text Transfer Protocol (HTTP).
With following example
var http = require('http');
//create a server object:
http.createServer(function (req, res) {
res.write('Hello World!'); //write a response to the client
res.end(); //end the response
}).listen(8080); //the server object listens on port 8080
This is the example to live demo
First, I am unable to comprehend their example like Where are they making (route) request so that they are receiving response?
Second by the definition, to make a request, using libraries like axios can be alternative?
third, when we make an api request, isn't the data transferred over http/https?
app.post("/", (req, res) => {
In short, Can someone please explain me in more human words the use of http package in express?
Update: I might be confusing this with express, I am used to using express and here we aren't using express
1- They aren't defining any route. That piece of code only creates a server running on port 8080 that when it's created or accessed on the home route (/) returns "Hello World". If you want to define routes you should take a closer look to a module called express that it's used by most of node users due to its simplicity and documentation (https://expressjs.com/en/starter/hello-world.html) In that link you have an example for creating the server and a basic route
2- Yes it can and should be because they are way better than the default from nodeJs. Take a look at axios or superagent, superagent it's better if you want to use formdata to send images or attachments.
3- By default, all servers created using http or express are http servers (don't have a certificate to encrypt the data so they aren't secure). If you want a https server, you can buy certificates or use https://letsencrypt.org/ this module that generates free SSL certificates with 1 month validation.
http module has multiple functions, it can be used to create a server, to make http requests and so on. It's up to you to decide which submodule from the package you want to use. Express is built over the http module making everything easier.
If you need more explanation, tell me and I will try to explain a little better.

Why must I include the host with URLs with node web requests?

No matter which library I use for node, they all require absolute URLs.
This means I need to either build a fetch curry, then pass that fetch through function chains to be able to make a request, OR I need to make a constant that is defined after figuring out whether im in production or development, then update that URL whenever my environments change.
Regardless, why does node require the hostname for URL's?
Is there a more seamless way to do server side requests anywhere in my app (including deep in function chains)?
A code example:
node index.js
index.js
let app = Express();
app.use('ace', (req, res) => foo());
app.use('somedata.json', (req, res) => res.status(200).send('{"hello": "world"}'));
foo.js
() => bar();
bar.js
() => bat();
bat.js
() => fetch('/somedata.json').then(console.log);
There is no such thing as a relative HTTP request. Any actual HTTP request must be a fully qualified URL. That's the HTTP specification. Because HTTP is stateless, there is no "base path" state associated with a given host.
Relative requests exist in a browser-type environment, but that's only because the browser pre-processes the request and, if it's not absolute, the browser adds on a default base path to make it absolute. All requests coming from a browser to a server are actually absolute URLs.
You could write your own function that did the same thing the browser does. You set a base path on it and then you direct all your requests through this function that checks the URL sent for the request and if it's not a fully qualified path, then the base path is added to it.
We could help you more specifically if you showed a real code example of the problem you're trying to solve. If it's one of dev or production environment, then you would typically establish some variables at startup base don which type of environment you're running in and then just have your code use those variables whenever it forms a request.

Redirect an http request from a remote server to a local server using nodejs

There is a feature in a tool called charles that allows you to map remote requests:
http://www.charlesproxy.com/documentation/tools/map-remote/
Basically, it can take any request to a server(even if you're not the one running it) and then makes a new request to another server, preserving the path and the query string. The response from the second server then overwrites the response from the first server.
I just want to know if there is a node module that can do this. I tried using http-proxy, but I have a feeling this map remote tool is a bit different than a proxy, since it seems like you must own both servers with a proxy.
EDIT: Tried using the http-proxy node module again, but can't seem to get it to work. Here's my code:
var http = require('http')
, httpProxy = require('http-proxy');
httpProxy.createServer({
hostnameOnly: true,
router: {
'www.stackoverflow.com': 'localhost:9000',
}
}).listen(80);
// Create your target server
//
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
My expectation is that when I go to www.stackoverflow.com or www.stackoverflow.com:80, it will instead redirect to my localhost:9000
No, what you are asking for is indeed a simple proxy. And no, you don't have to "own" both servers to run a proxy. You simply proxy the request, and at that point you can modify the data however you wish.
The proxy module you mention will work fine, and there are many others. You can also do this with simple Nginx config if you wish.
I made this pastebin with my solution:
http://pastebin.com/TfG67j1x
Save the content of the pastebin as proxy.js. Make sure you install dependencies in the same folder as the proxy.js file. (npm install http-proxy colors connect util --save)
When you run the proxy, it will:
start a new server listening on 8013, acting as a proxy server;
start a demo target server listening at 9013.
When accessing the demo target through the proxy, it modifies the "Ruby" string into "nodejitsu", for easy testing. If you are behind a corporate firewall/proxy, this script fails for now.
UPDATE: The problem with "headers already sent" was at line 32/33. It turns out that several errors occured on the same connection. When first error occurs, headers would be sent, when second error occurs, headers are already sent; as a result a "headers already sent" exception is raised and the server is killed.
With this fix the server no longer dies, but it still does not fix the source of the error, which is that NODE.JS cannot reach your target site. You must be behind another proxy/firewall and NodeJS would need to forward the HTTP request to a second proxy. If you normally use a proxy in your Browser to connect to Internet, my solution will fail. You did not specify this to be a requirement, though.
You may verify this by accessing through my proxy a server inside your network (no proxy required for it normally).
UPDATE2: You should not try to access http://localhost:8013 directly, but to set it as a proxy in your browser. Take notice of your original browser proxy settings (see above). Try and access then http://localhost:9013.
Did you add that proxy to your browser config? Otherwise the underlying OS would route your request directly to www.stackoverflow.com and there is no way your proxy is catching that.
Could you confirm that www.stackoverflow.com is ending up at your node.app at all? The name currently will resolve to the IP address that leads you to this website, so you would have to have made sure that name now resolves to your node.app. In this case, that probably means editing your hosts file.

Node.js: How to stream remote file through my server to the user?

There's a large binary file at somwhere.com/noo.bin
I want to send this to the user on my web app.
I don't want to save this file on my server and then serve it, wondering if there's a way to stream the file in which my web app acts as the proxy (the file will look like mysite.com/noo.bin)
Install request, then:
var http = require('http'),
request = require('request'),
remote = 'http://somewhere.com';
http.createServer(function (req, res) {
// http://somewhere.com/noo.bin
var remoteUrl = remote + req.url;
request(remoteUrl).pipe(res);
}).listen(8080);
Though I would have written exactly #LaurentPerrin's answer myself, for completeness sake I should say this:
The drawback in that method is that the request headers you are sending to somewhere.com are unrelated to the request headers your server got. For example: if the request sent to you has a specific value for Accept-Language, it is likely that (as the code stands) you are not going to specify the same value for Accept-Value when proxying from somewhere.com. Thus the resource might be returned to you (and then from you to the original requester) in the wrong language.
Or if the request to you comes in with Accept-Encoding: gzip, the current code will get the large file uncompressed, and will stream it back uncompressed, when you could have saved bandwidth and time by accepting and streaming back a compressed file.
This may or may not be of relevance to you.
If there are important headers you feel you need to pass, you could either add some code to explicitly copy them from your request to the request you are sending somewhere.com, and then copy relevant response headers back, or use node-http-proxy at https://github.com/nodejitsu/node-http-proxy.
An example for a forward proxy using node-http proxy is https://github.com/nodejitsu/node-http-proxy/blob/master/examples/http/forward-proxy.js

Resources