Skip to content

Instantly share code, notes, and snippets.

@vermaslal
Last active October 1, 2021 02:38
Show Gist options
  • Save vermaslal/c5ad7d2e52482444bf424c13076b0c7c to your computer and use it in GitHub Desktop.
Save vermaslal/c5ad7d2e52482444bf424c13076b0c7c to your computer and use it in GitHub Desktop.
Drop Root Privileges in Node.js when using port <1024

A common use for Node.js is to build web applications. Usually, we want these applications to listen on port 80. As a security precaution, most OS’s require root privileges for this to happen (e.g. OS X, Linux, BSD). To run a Node application this way, we need to do the following:

sudo node server.js

Then, the application runs as root for the rest of the session. There are potential security risks to this, though. What if there is a vulnerability in your application and a hacker starts controlling your app and doing naughty things with it? Thankfully, we can drop the user account running our process to a less secured user, such as our normal account. There are two methods on the process global which can handle this for us, .setgid() and .setuid().

Here’s a working example of this in action. We want to run this code AFTER we bind to port 80, so we run the code in a callback:

app.listen(80, 'localhost', null, function() {
  // Listening
  try {
    console.log('Old User ID: ' + process.getuid() + ', Old Group ID: ' + process.getgid());
    process.setgid('users');
    process.setuid('tlhunter');
    console.log('New User ID: ' + process.getuid() + ', New Group ID: ' + process.getgid());
  } catch (err) {
    console.log('Cowardly refusing to keep the process alive as root.');
    process.exit(1);
  }
});

The results of .getuid() and .getgid() will be numeric, and aren’t really useful, other than showing that something happened. They’ll probably both say 0 the first time (that’s root in *nix land), and then some bigger number afterwards.

Unsure of your username or group name? For shame! Run the following commands:

whoami
groups

Reference

const express = require('express')
const logger = require('mylogger')
const app = express()
const fs = require('fs');
const port = 80
app.get('/', (request, response) => {
var d = Date.now();
fs.writeFile(__dirname + '/tmp/' + d + '.txt', d, function (err) {
if (err) {
logger.error('Unable to write on file', err);
return response.send('Unable to write on file' + d)
}
response.send('Hello from Express! ' + d);
})
})
app.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
try {
logger.log('Old User ID: ' + process.getuid() + ', Old Group ID: ' + process.getgid());
process.setgid('myhttp');
process.setuid('myhttp');
logger.log('New User ID: ' + process.getuid() + ', New Group ID: ' + process.getgid());
} catch (err) {
logger.log('Cowardly refusing to keep the process alive as root.');
process.exit(1);
}
logger.log(`server is listening on ${port}`)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment