Node.js is a popular runtime to write apps for. These apps are often production quality apps that are used by many people. To make maintaining them easier, we’ve to set some guidelines for people to follow.
In this article, we’ll look at some security and performance considerations when building and running Node.js apps.
Take Care When Working with Child Processes
We’ve to be careful with inputs. This means we should sanitize to prevent any shell inject attacks. We don’t want attacks to run commands with a child process instance that we created from our app.
If we don’t take precautions, we can easily let attackers run commands remotely from our system by entering malicious inputs with unsanitized system commands.
Hide Error Details from Clients
Clients shouldn’t know about the parts of our system or any private data. Therefore, we should hide any information that’s exposed to errors.
We should ensure that we don’t return the entire error object to the client, which may contain some sensitive app details. Sensitive things include file paths, 3rd-party modules, internal workflows of an app, etc., which can be exploited by an attacker.
Configure 2FA for NPM or Yarn
We can use multi-factor authentication to access our private package repositories. This way, attacker’s can’t publish packages that are modified by them.
If these attacks go undetected, then our packages may be exposing data or doing something that we don’t want attackers to do to our system.
Modify Session Middleware Settings
We should remove any information that tells an attacker about our system. One of them if which web framework we use to develop our app. This is because attackers can use this information to look up how to attack our systems.
Therefore, we should hide things like the X-Powered-By
response header, which returns the framework that’s used by default in Express.
Avoid DOS Attacks by Explicitly Setting When a Process Should Crash
The Node process will crash when errors aren’t handled. Many suggest that the process should exit even if the error is handled.
If we leave our app to crash when entering certain inputs, then attackers can find the pattern of what inputs make our apps crash. This isn’t good since we don’t want them to find ways to crash our system.
Any time our app crashes, a critical alert should be sent so that we’re aware of it.
Prevent Unsafe Redirects
We should validate user input to prevent malicious redirects so that attackers can’t launch phishing scams to steal user credentials.
Avoid Publishing Secrets to the NPM Registry
Secrets should never in the NPM registry. Otherwise, we increase the chance that we expose the secrets to the public. We can use npmignore
file to blacklist specific files or folder, or the files
array in package.json
can act as a whitelist.
Don’t Block the Event Loop
Since Node.js is single-threaded, we shouldn’t run any synchronous code that takes a long time. This way, we won’t block the event loop so that other pieces of code will run.
Whenever we have long-running processes, we should use asynchronous code like promises so that these kinds of code only runs when the app get a result.
Otherwise, one request can hang the app that thousands of users are using.
Prefer Native JS Methods Over 3rd-Party Utilities Like Lodash
If a function is available in the JavaScript standard library, then we should use it. This way, we need fewer dependencies and therefore, our app will run faster.
Native JavaScript functions are now about 50% faster than utility libraries.
Conclusion
We should validate the inputs of child processes to eliminate risks from malicious commands being entered and injected by attackers. Unsafe redirects should be checked so that users won’t be redirected to malicious sites.
Also, we should hide any information that may be useful to attacks like what framework is used, stack traces, etc. Secrets should never be in the NPM registry.
Performance-wise, we should use JavaScript standard libraries as much as possible to increase performance to reduce the number of dependencies used.