This is Part 2 of my express.js security blog post. Like any application, developers should make sure their express.js applications are secure and resilient to errors. In this post, I’ll go over some Npm packages that I have battle-tested in production. These are libraries I’ve used with success in securing my express.js applications.
By default, Express adds the x-powered-by header, which can show potential attackers what kind of server you are running and expose a vulnerability in Node (if you are running an older version). To remove the x-powered-by header, check out hide-powered-by, which is part of the helmet package.
Express can be vulnerable to parsing large payloads. For example, someone could post large payloads, which could potentially DDOS your server. To prevent that from happening, use express-content-length-validator.
When accessing req.body or req.query from incoming requests, Express arranges keys that are of equal value in an array. This can cause uncaught exceptions if your code is not expecting an array. A package that I have relied on to mitigate the problem of HTTP parameter pollution attacks is hpp.
If you are using cookies as an authentication method or allowing form submissions, your server may be vulnerable to cross-site request forgery (CSRF). CSRF is malicious code that executes a request via image tags or XSS. To prevent CSRF attacks, share a token between the web page and server to know that the request is not malicious. The csurf package handles this nicely in Express. For more information on CSRF, this is a great blog post.
Another way to prevent XSS is setting a Content Security Policy (CSP). When you need to prevent unwanted content being injected into your web app, such as XSS vulnerabilities, unintended frames, malicious frames, etc., the package helmet via helmet-csp has it covered. For more on how CSP works, check out content-security-policy.com.
If your app needs to be configured to allow specific domains or all domains to be able to make requests to it via browser, this can be done by setting cross-origin resource sharing (CORS) headers control. A package that does this well is cors. For more information on CORS, see this post.
If your express app is dealing with sensitive data that needs to be encrypted over https, see the express-enforces-ssl package. This package enforces HTTPS connections on any incoming requests. In case of a non-encrypted HTTP request, express-enforces-ssl automatically redirects to an HTTPS address using a 301 permanent redirect. One can also configure this at an NGINX level.
If you need to allow or restrict endpoints based on IP, express-ipfilter provides a great solution.
The nsp tool created by the Node Security Project will find packages that have vulnerabilities in your package.json.
Anytime I’m doing anything async, I use bluebird as my go-to library. I end up wrapping other libraries with it if they only provide a callback api. This is a great post that goes into depth on that scenario. A note on promises, many people say they swallow errors and that's why they prefer callbacks. When using bluebird, always make sure you handle catch statements on any chainable promise, and log the error. (More on logging libs later on.)
If I’m doing any intense operations on lists or hashes, lodash most likely provides a safer, higher-performing method.
It’s important to profile your express app to understand where there are bottlenecks. Response-time adds a X-Response-Time header to responses.
Any secure express app is well-tested and leverages code quality tools. One of my favorite code quality tools is eslint. For my testing with Express apps, I use mocha, chai, type-detect, supertest, sinon, and supertest-as-promised.
If your team is writing a custom regular expression, make sure it doesn't exploit Regular expression Denial of Service - ReDoS.
When your Express app receives a kill signal from a deploy or if there's an uncaught error and you need to restart your application, the Npm package express-graceful-exit has worked well for me in the past to shutdown my app gracefully. It's also important to make sure any hanging connections to external services are also closed. In my apps, I have events on uncaughtException, as well as SIGTERM and SIGINT, which exit the process cleanly before a restart.
Finally, it's important to know what your Express app should be doing in production. For example, Express shouldn't be serving static files. Instead, use a cdn, or configure with nginx with tryfiles. Also, I recommend running your apps in production with PM2 in clustermode. If you need to have state in any of your express apps, make sure that state is shared in redis.