Even more changes to "How to create static HTTP server in Node.JS" article.
This commit is contained in:
parent
99c08a5aa7
commit
adc9a292ed
1 changed files with 69 additions and 11 deletions
|
@ -103,7 +103,7 @@ But what if that index file doesn't exist? We will then need to serve 404 error
|
||||||
console.log("Started server at port " + port + ".");
|
console.log("Started server at port " + port + ".");
|
||||||
});
|
});
|
||||||
{% endcodeblock %}
|
{% endcodeblock %}
|
||||||
Note, that in static HTTPS servers, files are determined from resource URL.
|
Note, that in static HTTP servers, files are determined from resource URL.
|
||||||
{% codeblock server.js lang:javascript %}
|
{% codeblock server.js lang:javascript %}
|
||||||
//WARNING!!! PATH TRAVERSAL
|
//WARNING!!! PATH TRAVERSAL
|
||||||
var http = require("http");
|
var http = require("http");
|
||||||
|
@ -111,7 +111,7 @@ Note, that in static HTTPS servers, files are determined from resource URL.
|
||||||
var port = 8080;
|
var port = 8080;
|
||||||
var server = http.createServer(function (req, res) {
|
var server = http.createServer(function (req, res) {
|
||||||
var filename = "." + req.url;
|
var filename = "." + req.url;
|
||||||
if(req.url == "/") filename = "./index.html";
|
if(filename == "./") filename = "./index.html";
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
if(err.code == "ENOENT") {
|
if(err.code == "ENOENT") {
|
||||||
|
@ -138,7 +138,7 @@ Note, that in static HTTPS servers, files are determined from resource URL.
|
||||||
console.log("Started server at port " + port + ".");
|
console.log("Started server at port " + port + ".");
|
||||||
});
|
});
|
||||||
{% endcodeblock %}
|
{% endcodeblock %}
|
||||||
But we have introduced path traversal vulnerability! (being able to access file outside the web root) To mitigate that, we'll use a regular expression, that removes all dot-dot-slash sequences from file name:
|
But we have introduced path traversal vulnerability (being able to access file outside the web root)! To mitigate that, we'll use a regular expression, that removes all dot-dot-slash sequences from file name:
|
||||||
{% codeblock server.js lang:javascript %}
|
{% codeblock server.js lang:javascript %}
|
||||||
//Path traversal mitigated
|
//Path traversal mitigated
|
||||||
var http = require("http");
|
var http = require("http");
|
||||||
|
@ -146,8 +146,8 @@ But we have introduced path traversal vulnerability! (being able to access file
|
||||||
var port = 8080;
|
var port = 8080;
|
||||||
var server = http.createServer(function (req, res) {
|
var server = http.createServer(function (req, res) {
|
||||||
var filename = "." + req.url;
|
var filename = "." + req.url;
|
||||||
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"/").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
if(req.url == "/") filename = "./index.html";
|
if(filename == "./") filename = "./index.html";
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
if(err.code == "ENOENT") {
|
if(err.code == "ENOENT") {
|
||||||
|
@ -184,8 +184,8 @@ That might work fine for HTML files, but if you try other files, there will be c
|
||||||
var port = 8080;
|
var port = 8080;
|
||||||
var server = http.createServer(function (req, res) {
|
var server = http.createServer(function (req, res) {
|
||||||
var filename = "." + req.url;
|
var filename = "." + req.url;
|
||||||
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"/").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
if(req.url == "/") filename = "./index.html";
|
if(filename == "./") filename = "./index.html";
|
||||||
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
|
@ -224,8 +224,8 @@ But with query strings, it will fail. To prevent that, we'll be using WHATWG URL
|
||||||
var server = http.createServer(function (req, res) {
|
var server = http.createServer(function (req, res) {
|
||||||
var urlObject = new URL(req.url, "http://localhost");
|
var urlObject = new URL(req.url, "http://localhost");
|
||||||
var filename = "." + urlObject.pathname;
|
var filename = "." + urlObject.pathname;
|
||||||
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"/").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
if(req.url == "/") filename = "./index.html";
|
if(filename == "./") filename = "./index.html";
|
||||||
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
|
@ -274,8 +274,8 @@ It's nearly finished! But encoded URLs will not work. To fix that, we will use `
|
||||||
res.end("400 Bad Request");
|
res.end("400 Bad Request");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"/").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
if(req.url == "/") filename = "./index.html";
|
if(filename == "./") filename = "./index.html";
|
||||||
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
fs.readFile(filename, function(err, data) {
|
fs.readFile(filename, function(err, data) {
|
||||||
if(err) {
|
if(err) {
|
||||||
|
@ -303,6 +303,64 @@ It's nearly finished! But encoded URLs will not work. To fix that, we will use `
|
||||||
console.log("Started server at port " + port + ".");
|
console.log("Started server at port " + port + ".");
|
||||||
});
|
});
|
||||||
{% endcodeblock %}
|
{% endcodeblock %}
|
||||||
|
There is still one problem - the leak of "server.js" file. We can add a condition:
|
||||||
|
{% codeblock server.js lang:javascript %}
|
||||||
|
//Source code leakage mitigated
|
||||||
|
var http = require("http");
|
||||||
|
var fs = require("fs");
|
||||||
|
var mime = require("mime-types");
|
||||||
|
var path = require("path");
|
||||||
|
var port = 8080;
|
||||||
|
var server = http.createServer(function (req, res) {
|
||||||
|
var urlObject = new URL(req.url, "http://localhost");
|
||||||
|
var filename = "";
|
||||||
|
try {
|
||||||
|
filename = "." + decodeURIComponent(urlObject.pathname);
|
||||||
|
} catch(ex) {
|
||||||
|
//Malformed URI means bad request.
|
||||||
|
res.writeHead(400, "Bad Request", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("400 Bad Request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filename = filename.replace(/\\/g,"/").replace(/\/\.\.?(?=\/|$)/g,"/").replace(/\/+/g,"/"); //Poor mans URL sanitizer
|
||||||
|
if(filename == "./") filename = "./index.html";
|
||||||
|
var ext = path.extname(filename).substr(1); //path.extname gives "." character, so we're using substr(1) method.
|
||||||
|
if(filename == "./" + path.basename(__filename)) {
|
||||||
|
//Prevent leakage of server source code
|
||||||
|
res.writeHead(403, "Forbidden", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("403 Forbidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.readFile(filename, function(err, data) {
|
||||||
|
if(err) {
|
||||||
|
if(err.code == "ENOENT") {
|
||||||
|
//ENOENT means "File doesn't exist"
|
||||||
|
res.writeHead(404, "Not Found", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("404 Not Found");
|
||||||
|
} else {
|
||||||
|
res.writeHead(500, "Internal Server Error", {
|
||||||
|
"Content-Type": "text/plain"
|
||||||
|
});
|
||||||
|
res.end("500 Internal Server Error! Reason: " + err.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.writeHead(200, "OK", {
|
||||||
|
"Content-Type": mime.lookup(ext) || undefined
|
||||||
|
});
|
||||||
|
res.end(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
server.listen(port, function() {
|
||||||
|
console.log("Started server at port " + port + ".");
|
||||||
|
});
|
||||||
|
{% endcodeblock %}
|
||||||
We have now very simple HTTP static server, serving at *localhost:8080*.
|
We have now very simple HTTP static server, serving at *localhost:8080*.
|
||||||
|
|
||||||
**Wait... Did we forget about [SVR.JS](https://svrjs.duckdns.org)?** SVR.JS is a web server running on Node.JS, that supports not only static file serving, but also directory listings, path rewriting, complete URL sanitation, HTTPS, HTTP/2.0, expandability via mods and server-side JavaScript, and it's configurable.
|
**Wait... Did we forget about [SVR.JS](https://svrjs.duckdns.org)?** SVR.JS is a web server running on Node.JS, that supports not only static file serving, but also directory listings, path rewriting, complete URL sanitation, HTTPS, HTTP/2.0, expandability via mods and server-side JavaScript, and it's configurable.
|
||||||
|
|
Reference in a new issue