does nodejs prevent directory/path traversal by default? - javascript

Expressjs's "express.static()" prevents directory/path traversal by default but I thought Nodejs does not have any protection towards directory/path traversal by default?? Recently trying to learn some web development security(directory/path traversal) and I created this:
const http = require("http");
const fs = require("fs");
http
.createServer(function (req, res) {
if (req.url === "/") {
fs.readFile("./public/index.html", "UTF-8", function (err, data) {
if (err) throw err;
res.writeHead(200, { "Content-Type": "text/html" });
res.end(data);
});
} else {
if (req.url === "/favicon.ico") {
res.writeHead(200, { "Content-Type": "image/ico" });
res.end("404 file not found");
} else {
fs.readFile(req.url, "utf8", function (err, data) {
if (err) throw err;
res.writeHead(200, { "Content-Type": "text/plain" });
res.end(data);
});
}
}
})
.listen(3000);
console.log("The server is running on port 3000");
to simulate directory/path traversal security vulnerability but I tried to use "../../../secret.txt" and when I check "req.url", it shows "/secret.txt" instead of "../../../secret.txt" and I also tried using "%2e" & "%2f", it still doesn't work, I still can't get "secret.txt"
(My folder Structure)
- node_modules
- public
- css
- style.css
- images
- dog.jpeg
- js
- script.js
index.html
- package.json
- README.md
- server.js
- secret.txt

As per the documentation of express.static [1], which leads to the docs of the serve-static module [2], the directory you provide is the root directory, meaning it's intentionally made impossible to access anything outside of it.
To serve static files such as images, CSS files, and JavaScript files, use the express.static built-in middleware function in Express.
The function signature is:
express.static(root, [options])
The root argument specifies the root directory from which to serve static assets. For more information on the options argument, see express.static. [3]
[1] https://expressjs.com/en/starter/static-files.html
[2] https://expressjs.com/en/resources/middleware/serve-static.html#API
[3] https://expressjs.com/en/4x/api.html#express.static
Not related, but fyi: the path you're providing to fs etc. is relative to where the script is called from.
For example, if you call node server.js from the root folder of the application, the path "./public/index.html" should work fine, but if you're calling it from a different path, it will fail, e.g. node /home/user/projects/this-project/server.js.
Thus, you should always join the path with __dirname, like so:
+const path = require("path");
-fs.readFile("./public/index.html", "UTF-8", function (err, data) {
+fs.readFile(path.join(__dirname, "./public/index.html"), "UTF-8", function (err, data) {
}
this makes the path relative to the directory of the current file you're trying to access it from, which is what you expect.

Related

html page not loading when running javascript server

So I'm creating a game with HTML, css, and javascript, and I'm trying to incorporate template engines/ajax in this task. I made a server which if I run in cmd and then open up google chrome and type 'localhost:3000' in the address line, it is supposed to direct me to the main.html page.
However, when I type 'node server.js' in cmd, it runs properly but when I enter 'localhost:3000' in the browser it says the page does not exist. I'm not sure what went wrong. If I were to manually double click on the html file in my folder, it works, but I'm trying to get it to load by running a server.
I have three folders (img, node_modules, and pages) and 2 json packages which were created by installing express and pug. It's hard to explain my folder paths and code here, so I have a link to a folder containing my files/sub folders and it also gives a clearer view of the path in my directory: https://github.com/jessaisreal/game
It wouldn't let me upload the node_modules folder as it was too big, but I automatically got it from typing 'npm init', 'npm install express' and 'npm install pug' into the cmd line in the folder.
I'm assuming something is wrong with my server.js file or the way my folders are set up. I'm really desperate to get my program working and would appreciate any help. I have no idea why my html page isn't loading. I'm also not sure if I handled the GET request for getting specific fonts correctly.
I cut down my program as much as I could. There are several html and css files, but only included create and main here for simplicity. Again, I would appreciate any help or a push in the right direction!!
server.js:
const http = require('http');
const fs = require("fs");
const pug = require("pug");
//user pug functrion to render through the create Page
const renderMain = pug.compileFile('pages/main.pug');
const renderCreate = pug.compileFile('pages/create.pug');
//Helper function to send a 404 error
function send404(response){
response.statusCode = 404;
response.write("Unknown resource.");
response.end();
}
// Helper function to send 500 server error;
function send500(response){
response.statusCode = 500;
response.write("Server error.");
response.end();
}
// initilize the server
const server = http.createServer(function (request, response) {
//console.log(request.method+" -> "+request.url); test about the income request
// handle the get request
if(request.method === "GET"){
if(request.url === "/" || request.url === "/main"){
let data = renderHome("./pages/main.pug",{})
response.statusCode = 200;
response.end(data);
}else if(request.url === "/main.js"){
//read main.js file and send it back
fs.readFile("main.js", function(err, data){
if(err){
send500(response);
return;
}
response.statusCode = 200;
response.setHeader("Content-Type", "application/javascript");
response.write(data);
response.end();
});
}else if(request.url === "/main.css"){
//read css file
fs.readFile("main.css", function(err, data){
if(err){
send500(response);
return;
}
response.statusCode = 200;
response.setHeader("Content-Type", "text/css");
response.write(data);
response.end();
});
}else{
send404(response);
return;
}
}
});
//Server listens on port 3000
server.listen(3000);
console.log('Server running at http://127.0.0.1:3000/');
I highly suggest that you use expressjs for better organizing your project and this issue will be gone

Generate static html files from express server

I've got a nodejs web project running with express-js and ejs. After making it, it appered to me that it can be host throught static html files on Netlify instead of an nodejs app on Heroku. Indeed, the data only change each month so I only have to build it once a month. Like that, it will be the best hosting option regarding the price.
Is there a way (npm package, GitHub action, ...) to compile an entire express server + ejs application into a folder of static html/css files in order to be hosted on Netlify ?
I've been looking for a while and I couldn't find anything solving my problem.
Thanks for your help.
You can loop through all of the routes, and execute app.render for each of them, then store results in a file.
Sample code:
//express server should be started before this
const fs = require('fs')
['/', '/about', '/contact'].forEach( path => {
app.render(path, {
// optional metadata here
}, (err, res) =>{
if (err)
console.log('Error rendering ' + path, err)
else {
fs.writeFile(__dirname + '/public/' + path + '.html', res, function(err, res) {
if (err)
console.log('error saving html file', path, err)
})
}
})
})

Serving images via http createServer

I am attempting to create a basic webserver with Node.JS, but running into an issue with properly serving images.
var server = http.createServer(function (request, response){
if(request.url === '/') {
fs.readFile('public/index.html', 'utf8', function(errors, contents){
response.write(contents);
response.end();
});
} else {
fs.readFile("public" + request.url, 'utf8', function(errors, contents){
if (!errors) {
response.end(contents);
} else {
console.log('Failed to read file: /public' + request.url);
response.writeHead(404);
response.end();
}
});
}
});
Everything works fine, apart from if you go to view the image it attempts to download it (which I believe is corrupted - cant open it), which is not what I want, I wish for the images to be served properly in the browser (not via a tag)
Bonus points: I need to be able to correctly give the proper headers (Do I need just a switch statement and set them via that?), as chrome is giving off warnings
Resource interpreted as Stylesheet but transferred with MIME type text/plain: "http://localhost/css.css".
You can use expressjs : http://expressjs.com/
const express = require('express')
const app = express()
app.get('/', (req, res) => res.sendFile('public/index.html'))
app.use(express.static('public'))
app.listen(8080, () => console.log('Example app listening on port 8080!'))
NB :
Your code is unsafe : "public" + request.url. Your users can get all files of your server : fs.readFileSync("public/../../../README.txt");
You will get error on parent folder.
Error: ENOENT: no such file or directory, open 'C:\README.txt'.

Saving and loading data in a node.js server and passing it to an html5 file

So I'm very new to node.js and javascript, and i made a server that works great by loading up an html file on request. This html file does not contain any of it's own data, it simply sources from the internet and displays some images and text i wrote. I've decided to make the site play an audio file when it is opened. I know this is done easily with the <audio> tag in html5 but the src="" lets me take a file from the computer and place it there, of course when i open the site from another computer the file obviously isn't found and thus isn't played. I figure the audio file must be kept as a variable on the server and passed into the html file's <audio src= > tag. How do i do this? It is an .mp3(but i can get it to any other audio format) file about 30 seconds long. I just want it to play when the site is loaded from another computer(over the internet). Also how would i go about doing the same with pictures or any other data that i don't want to source from the internet but rather keep as data in my server?
var http = require('http');
var fs = require('fs');
var simpleServer = http.createServer(function(request, response){
response.writeHead(200, {"Content-Type":"text/html"});
fs.readFile('./Picture.html', null, function(error, data){
if(error){
response.writeHead(404);
} else{
response.write(data);
}
response.end();
})
});
simpleServer.listen(80, '0.0.0.0', function() {
console.log('Listening to port: ' + 80);
});
console.log("Server running...");
Short Answer
Bypassing using HTML altogether, you can also simply serve the audio file instead of Picture.html:
fs.readFile("./audiofile.mp3", function(error, data) {
if (error) {
response.writeHead(404);
} else {
response.writeHead(200, { "Content-Type": "audio/mpeg"});
response.end(data, 'utf-8');
}
});
Note:
You will have to replace the filename audiofile.mp3 and the content type audio/mpeg to their appropriate values for the file you want to send.
Check Mozilla's Complete List of MIME Types for a full list of file extensions and their associated content types.
Better Answer:
The http module is fairly low-level and is unnecessarily complicated if you're learning.
You can install express.js to your project using the command npm install express --save.
With express your code simplifies to:
const express = require('express');
const app = express();
const port = 80;
app.get('/', (request, response) => {
response.sendFile(__dirname + '/Picture.html');
});
// Anything put in the public folder is available to the world!
app.use(express.static(__dirname + '/public'));
app.listen(port, () => {
console.log(`Listening on port: ${port}`)
});
Then you just have to place all your files into a folder called "public" under your project directory and you can call them from HTML!

Node.js - Loading Files in Runtime

I'm fairly new to Node.js and am having trouble understanding the way to go about loading libraries or files, in runtime.
Apparently, it is a bad idea to load files in runtime using Node.js's native "require" function because it is blocking i/o, and therefore should not be used from within request handlers. So, I'm assuming something like this is to be avoided:
var http = require('http').createServer(function(req, res) {
var file = require('./pages/'+req.url);
res.end();
}).listen(8080);
So then is there a way to require files in runtime, in a non-blocking/asynchronous way?
I don't think it would always be possible to load files in "boot time" rather than runtime because like in the example above, the only way to know what file to load/require is by getting the name through the req.url property.
So that seems like the only option. Unless, all the files in the folder are preloaded and then called upon by name, in the callback (By using fs.readdirSync or something to iterate through all the files in the folder and compare the gotten files' names to the req.url property), but that seems wasteful. "Preloading" all the files in the folder (maybe around 50 files) and then only using 1 of them, doesn't seem like a good idea. Am I wrong?
Either way, I would just like to know if there is a way to require files in runtime in a better, non-blocking/asynchronous way.
Thank you!
The function require() is generally used for caching modules or configuration files before most of your application runs. You can think of using require() somewhat like this:
var file = fs.readFileSync('/path');
// depending on the file type
eval(file);
JSON.parse(file);
The reason it is done this way is so that dependencies are loaded in order. If you want to read a file after initializing the application, you should use a asynchronous read, and respond in the callback like this:
var http = require('http');
http.createServer(function(req, res) {
fs.readFile('./pages/' + req.url, function(err, data) {
res.end(data);
});
}).listen(8080);
If you needed to check if a file existed, then you could use fs.stat() to check the existence of a file, rather than querying the directory.
var http = require('http');
http.createServer(function(req, res) {
var file = './pages/' + req.url;
fs.stat(file, (err, stats) {
if (err || !stats.isFile()) {
res.writeHead(404);
res.send();
return;
}
fs.readFile(file, function(err, data) {
res.writeHead(200);
res.end(data);
});
});
}).listen(8080);

Categories

Resources