Security Best Practices for Individual Services and Applications Learn what needs to be done when hardening your application against all sorts of possible attacks in this tutorial by Parth Ghiya, an expert in multiple technologies, including mobile, web, and enterprise.

8 Security Mistakes Computer Users Make

A microservice architecture shifts around complexity. Instead of having a single and very complicated system, there are a bunch of simple services with complicated interactions. The goal is to make sure that complexity stays in check and within boundaries. Security is really hard to get right.

There are countless ways to break into an application. Node.js is no different. In this article, you will look at techniques to prevent security vulnerabilities. This article is meant to act as a basic checklist to ensure that your microservice addresses some of the biggest security threats. You can check out the complete code for this article at https://github.com/PacktPublishing/TypeScript-Microservices/tree/master/Chapter10/.

Checking for known security vulnerabilities

Due to a wealth of modules available in npm, you can directly work on the application and rely on the ecosystem for ready-made solutions. However, due to huge modules, larger security vulnerabilities can occur at any time even for mature popular frameworks. In this section, you will look at some valuable tools that ensure no vulnerabilities are present in the packages that the application relies on even while updating:

Auditjs

This is a simple utility that audits an npm project using the OSS index v2 REST API to identify known vulnerabilities and outdated package versions. Using this is very simple:

  1. Install it as a dev dependency  .
  2. Add audit scripts in the npm scripts:
  1. Run the npm run audit command. The full example can be seen in the extracted folder under the chapter 10/auditjsfolder.

Snyk.io

This is another module that you can use to vet any modules against the vulnerability database maintained by synk.io. The major advantage of this module is that you do not need to install this for auditing. This module can be used as a pre-check before using any third-party module:

  1. Install it globally – npm install snyk –g.
  2. Once installed, you need to authenticate it by hitting snyk auth.
  3. Once snykis set up, you can vet any module using synk test <module_name>.

The following are some useful commands:

snyk wizardFinds and fixes known vulnerabilities in a project
snyk protectApplies patches and suppresses vulnerabilities
snyk monitorRecords the state of dependencies so that whenever new vulnerabilities or patches are launched, you can be alerted

Preventing brute force attacks or flooding by adding rate limiters

Brute force attacks are common and often serve as a last resort for the hacker. They systematically enumerate all possible candidates for a solution and check whether each candidate satisfies the problem statement or not. To protect against this kind of attack, you have to implement some kind of rate limiting algorithm, which will effectively block an IP address from making an outrageous amount of requests, thus blocking the possibility of accidentally crashing the application.

You can find the rate-limiting implementation under the Chapter 10/rate-limiter folder, where you can use the rate limiting algorithm with the Redis database. Now, follow these steps:

  1. Install express-limiter and redis:
  1. Create the redis client and set express-limiter:
  1. Now, run the program. It will limit requests to 100 requests per hour, after which it will start to throw 429: Too Many Requests.

Protecting against evil regular expressions

One of the most commonly occurring vulnerabilities is a poorly formed regular expression. A regex, if it takes exponential time when applied to non-matching inputs, is termed an evil regex and should be prevented. An evil regex contains groupings with repetitions, alterations with overlappings, and words inside the repeated group. Here’s an example: Regex : (b+)+, ([a-zA-Z]+)*,(a|aa)+.

All these regexes are exposed to the input bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb!. These repetitions can be a hindrance as it may take seconds or even minutes to complete. Due to the event loop of Node.js, the execution won’t go ahead, which will effectively result in the server freezing as the application is completely stopped from running. To prevent such disasters, you should use the safe-regex tool (https://www.npmjs.com/package/safe-regex). It detects potentially catastrophic exponential time regular expressions.

You can check the source code in the safe-regex folder. You can also check the regex by typing node safe.js ‘<whatever-my-regex>’.

Blocking cross-site request forgeries

A common way to intrude in an application is by putting data into the application via unsafe sites through a common phishing technique known as cross-site request forgery. An intruder making a phishing attempt can initiate a request via a form or other input that creates a request for an application through inputs exposed by the application.

To harden the application against this kind of attack, you can use a CSRF token implementation. Every time a user makes a request, a new CSRF token is generated and added to the user’s cookie. This token should be added as a value to the inputs in an applications template, and this will be validated against the token the CSRF library generates when the user sends information. NPM provides the csurf module (https://www.npmjs.com/package/csurf), which can be used in express middleware directly and you can play accordingly with the csurf token.

Tightening session cookies and effective session management

The focus of the secure use of cookies cannot be understated in an application. This especially applies to stateful services that need to maintain a state across a stateless protocol such as HTTP. Express has a default cookie setting that can be configured or manually tightened to enhance security. There are the various options:

  • secret: A secret string with which the cookie has to be salted.
  • name: Name of the cookie.
  • httpOnly: This basically flags cookies so that they can be accessible by issuing a web server in order to prevent session hijacking.
  • secure: This requires TLS/SSL to allow a cookie to be used only in HTTPS requests.
  • domain: This indicates specific domains only, from which the cookie can be accessed.
  • path: The path cookie is accepted from an application’s domain.
  • expires: The expiration date of the cookie that is being set. If a timely expiration is not available, the resource consumption will be very high and resources will never be freed.

In the following example, you can securely set cookies using express-session and thus have effective session management. You can follow along with the example under typescript-express-session:

  1. Clone first-microservicefrom https://github.com/PacktPublishing/TypeScript-Microservices/tree/master/Chapter02/first-microservice, and install express-session and @types/express-session.
  2. In express.ts, add the following code, which will make your application use cookies with the following secured parameters:
This module effectively helps you handle stateful sessions by providing various options, such as cookie flags and cookie scopes.

Adding helmet to configure security headers

The helmet module (https://www.npmjs.com/package/helmet) is a collection of 11 security modules that prevents a varying number of attacks against an express microservice. It’s easy to use, as you just have to add two lines of code. Adding some basic configurations can help to protect the application against possible security mishaps. You can use helmet by simply adding this code:

The source code for this can be found in chapter-10/typescript-express-session.

The helmet module has a whopping 12 packages that act as some middleware to block malicious parties from breaking or using an application. These headers include headers for helmet-csp (headers for content security policy HTTP header), dns-prefetch protocols, frameguardshide-powered-byhpkphstsienoopennocachedont-sniff-mimetypereferrer-policyx-xss protectionsframeguard to prevent clickjacking, and so on.

Another option for securing headers is lusca (https://www.npmjs.com/package/lusca), which can be used in combination with express-session. An example can be found in the chapter-10 /express-lusca directory.

Avoiding parameter pollution

In Node.js, if there are no defined standards for handling multiple parameters of the same name, the de facto standard is to treat those values as an array. This is extremely useful because for a single name when the expected outcome is a string, it types changes to an array if multiple parameters with the same name are passed. If this isn’t accounted for in query handling, the application will crash and bring the whole thing down, making this a possible DoS vector. For example, check http://whatever-url:8080/my-end-point?name=parth&name=ghiya.

Here, when you try to read req.query.name, you expect it to be a string, but instead, you’ll get an array, [‘parth’,’ghiya’], which will bring down the application if not handled with care. To ensure that the application won’t fail you, you can do the following things:

  • Various policies implement polluting mechanisms differently; for example, some may take the first occurrence and some may take the last occurrence
  • Use TypeScript types to validate the request. If the types fail, stop the request by giving parameters errors
  • Ensure that parameters in HTTP GET, PUT, or POST are encoded
  • Strict Regexp must be followed in URL rewriting

Securing transmission

If an application has any moving parts (HTTP methods such as POST, PUT, and DELETE) that include anything right from logging or sending a tweet which mutates the information from the client, using HTTPs is a vital implementation to make sure that the information isn’t modified in mid-transit. Cost can be an easy excuse for not investing in an SSL certificate. But now there are new, completely free, SSL certificate resources, such as Let’s Encrypt (https://letsencrypt.org/).

Also, a Node.js application should not be directly exposed to the internet, and SSL termination should be handled prior to the request coming to Node.js. Using NGINX to do this is a highly recommended option, as it is specially designed to terminate SSL more efficiently than Node.js.

We already published a number of useful guides about NGINX on these security topics, such as:

And so on.

To effectively set an express application behind a proxy, refer to http://expressjs.com/en/4x/api.html#trust.proxy.options.table. Once the HTTP is set up, you can use nmapsslyze, or OpenSSL to test HTTP certificate transmission.

Preventing command injection/SQL injection

An injection attack can occur when an intruder sends text-based attacks that exploit the syntax of an interpreter. SQL injection consists of the injection of a partial or complete SQL query through user input, which can expose sensitive information and can be destructive as well. Similarly, command injection is a technique that can be used by an attacker to run OS commands on a remote web server. Through this approach, even passwords can be exposed. To filter against these kinds of attacks, you should always filter and sanitize user inputs. Using JavaScript statements such as eval is also another way to open up a door to injection attacks. To prevent these attacks, you can use node-postgres if you are using postgres (https://www.npmjs.com/package/pg), which provides positional query parameters. Common techniques to defend against injection include the following:

  • To escape SQL injection, one of the techniques that can be used is escaping user input. Many libraries provide this out of the box.
  • Parameterizing SQL queries is another way to avoid SQL injection, where you create the using positional query parameters and fill in the positional query parameters with values.
  • eval() with user input is one of the ways to inject commands and should not be used at all (in the next section, you’ll learn to write a linter, which will avoid this).
  • Similarly, the express application is vulnerable to MongoDB attacks. Not explicitly setting the query selector will result in your data being vulnerable to a simple query.

You have db.users.find({user: user, pass: pass}), where user and pass are coming from a POST request body. Now being type-less, you can simply pass query parameters inside this query, such as {“user”: {“$gt”: “”},”pass”: {“$gt”: “”}}, which will return all users along with their passwords. To resolve this, you need to explicitly pass the query selector, which will make your query db.users.find({user: { $in: [user] }, pass: { $in: [pass] }}).

TSLint/static code analyzers

In this section, you’ll look at one of the ways to analyze all the written code and check it against a list of security vulnerabilities. You’ll include this as one of the stages of your deployment plan. You’ll write a linter and have a .tslint file, where all the rules to be checked against are mentioned, and then you can run the lint.

TsLint is one way to check and validate the source code. It is a static analysis code tool that runs on Node.js in order to keep your source code clean, find possible bugs, uncover security issues, and enforce a consistent style across all your teams:

  1. Clone your first-typescript-microservices from earlier and inside it, add the following commands:
  1. Next, write jsonwith the basic rules that you want to evaluate it against. Copy the rules from https://github.com/Microsoft/tslint-microsoft-contrib/blob/master/recommended_ruleset.js.
  2. Next, write an initialization script:
  1. You can now leverage this script anywhere because when you run this, it will throw an output of all the errors found while evaluating against that rule set.
  2. You can add a –fixflag in the preceding script, which will automatically take the necessary measures in most cases. You can find the source code under the chapter 10/tslinter
If you found this article interesting, you can explore Typescript Microservices to leverage the power of microservices to build robust architecture using reactive programming and Typescript in Node.js. This book is an end-to-end guide that shows you the implementation of microservices from scratch; right from starting the project to hardening and securing your services.

Addendum for Node.js 10 Upgrade

NPM 6 gives protection against insecure code, which is used by more than 10 million JavaScript developers across the world to download more than 900 million packages of reusable modular code per day. These new protections include automatic alerts if anyone attempts to use open source code with known security issues and another option for npm audit, a command which allows developers to analyze complex and interdependent code to pinpoint specific vulnerabilities in the code.

Whenever you install npm, you will get the following alert if there is a security issue:

Security Best Practices for Individual Services and Applications

Whenever you npm audit afterward, you will get a detailed report like this:

Security Best Practices for Individual Services and Applications

You can further fix by running command npm run audit fix.

 

About Parth Ghiya

Parth Ghiya loves technologies and enjoys learning new things and facing the unknown. He has shown his expertise in multiple technologies, including mobile, web, and enterprise. He has been leading projects in all domains with regard to security, high-availability, and CI/CD. He has provided real-time data analysis, time series, and forecasting solutions too.In his leisure time, he is an avid traveler, reader, and an immense foodie. He believes in technological independence and is a mentor, trainer, and contributor.

View all posts by Parth Ghiya

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.