diff --git a/source/_posts/How-to-create-static-HTTP-server-in-Node-JS.md b/source/_posts/How-to-create-static-HTTP-server-in-Node-JS.md index 5a1f397..d3d18d8 100644 --- a/source/_posts/How-to-create-static-HTTP-server-in-Node-JS.md +++ b/source/_posts/How-to-create-static-HTTP-server-in-Node-JS.md @@ -305,7 +305,7 @@ It's nearly finished! But encoded URLs will not work. To fix that, we will use ` {% 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 + //Source code leakage mitigated, but THERE IS A "%00" PROBLEM! var http = require("http"); var fs = require("fs"); var mime = require("mime-types"); @@ -362,6 +362,65 @@ There is still one problem - the leak of "server.js" file. We can add a conditio console.log("Started server at port " + port + "."); }); {% endcodeblock %} +Oh no! Our server may be vulnerable to null byte injection! You can remove null bytes and "%00", like this: +{% codeblock server.js lang:javascript %} + //Null byte injection mitigated! + var http = require("http"); + var fs = require("fs"); + var mime = require("mime-types"); + var path = require("path"); + var os = require("os"); + 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(/\0|%00/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)) || (os.platform() == "win32" && filename.toLowerCase() == ("./" + path.basename(__filename)).toLowerCase())) { + //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*. **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.