How to Protect Against Vulnerable and Outdated Components: Web Security Blog Series

Nikola Mitrović Categories: Knowledge Base Date 04-Feb-2022 5 minutes to read
How To Protect Against Vulnerable And Outdated Components Web Security Blog Series

Table of contents

    Introduction

    When we talk about web security, the first association that comes to mind of most developers is OWASP Top 10, which is a standard document for web application security, and for increasing awareness about it among web developers.

    Most experiences while working on software development projects has taught us that many developers don’t pay much attention to web security until there is some problem or a red flag. However, when such a situation occurs, it may be too late already, and some bad actors have already extracted some secret information about users, their credentials, tokens, private data and so on.

    Here at Vega IT, we strongly believe that web security is a continuous process, and that all of us should be aware of the potential risks and threats on the web, at least on a higher level.

    It doesn’t matter if you are a FE dev or a BE dev inside the team, there are web security rules that we all apply when working on our projects, which is how we ensure the best possible quality of the software delivered to our clients.

    That is why we are starting a Web Security Blog Series through the perspective of a JS developer. Our goal is to bring that awareness to others, and to emphasise how important it actually is when thinking about web security.

    What Is OWASP Top 10?

    As we know, OWASP Top 10 is the list of the 10 most common application vulnerabilities:

    This list shows risks, impacts, and countermeasures against those vulnerabilities.

    Throughout our blog series, we’re going to explore each of them, give you a detailed explanation about these vulnerabilities, and most importantly, tell you about the advanced techniques we use to prevent them.

    As part 1 of the blog series, we’re going to start with the protection against Vulnerable and Outdated Components.

    Detect & Fix Vulnerable and Outdated Components

    Regardless of you being a FE dev or a BE Node.js dev, most of us who work with JavaScript use npm as a package manager. Npm is the most popular package manager currently (although yarn is coming up real close), so all of us must be aware of some security vulnerabilities which we may encounter when using npm.

    You can not manage it if you can’t measure it!

    To fix some of the security issues that we may have, first we need to detect them.

    How many times has it happened that you work on a project, install a few packages from the web with npm install command, and get something like this at the bottom of a terminal:

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu-1.jpg

    In most cases, we ignore these warnings and blissfully continue working on our cool new feature, but we should take these messages more seriously since there are numerous cases of this vulnerability exposed, like the eslint-scope incident.

    Ignoring warnings like this, we may put our project in a real danger, so there has got to be a way of making those warnings go away.

    The way we can easily check project dependencies for security vulnerabilities is with the npm audit command. After we execute this command inside a terminal, we should get info about the number of vulnerabilities, the list of all of them, their severity and reasoning, for example:

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu2.jpg

    Npm gets friendly enough to offer a command which can help us fix those issues, so we can run the npm audit fix command inside our terminal and check the results afterward.

    ⚠️ Be careful if using audit fix with --force flag, it can lead to breaking changes with newly updated dependencies.


    After that, we should get a detailed npm report, which will tell us the results of the audit fix.

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu3.jpg

    As we can see, the situation is a little bit better than before, but for some of the packages, the audit fix didn’t do the trick, so we need to fix those vulnerabilities manually. The following are the simple steps in order to do that:

    1. Run npm update to upgrade your critical dependencies to the next semver version

    2. Run npm ls to check if there are transitive dependencies that are still vulnerable

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu4.jpg

    Now we have successfully identified, based on the audit report and the dependency tree, that the package webpack-dev-server is depending on the package chokidar which is depending on the malicious version 3.1.0 of the package glob-parent.

    3. Manually install next available version of the package that has malicious transitive dependency

    In order to fix this, we first look for the next version of the package which is affected and is at the highest level of the dependency tree, in our case the webpack-dev-server. We can find out that information with the npm info command. At the time of writing this blog, the highest stable version of the webpack-dev-server package was 4.2.0, so we can try to install that version with
    npm install -D webpack-dev-server@latest and check if the vulnerability is gone.

    In some instances, even this won’t help us if the highest available version of the package is still using malicious transitive dependency, so we need to force it to use the correct version of transitive dependency.

    4. Add a resolutions key in your package.json file

    {    "resolutions": {        "<vulnerable_transitive-package_name>": "^1.2.5"     }}

    If you are not sure which is the latest version, you should install the transitive dependency, and an audit report will inform you in which version the issue is patched.


    To apply our dependency resolution, we need to use NPM Force Resolutions, and run a npx npm-force-resolutions command.


    ⚠️ Be careful with forcing version resolutions, since it can force incompatible versions between packages and break functionality of your app.


    5. We can take this even a step further, and ensure that dependency resolutions are always running. The way to do that is to run npx npm-force-resolutions as a part of a preinstall script.

    {    "scripts": {        "preinstall": "npx npm-force-resolutions"     }}

    All we need to do after that is to reinstall packages with rm -rf node_modules && npm install and check again with npm audit if the dependency is still vulnerable, or npm ls to check if the correct version of a specific dependency is installed.

    Then we can be sure that we have successfully resolved the issues from a vulnerable package, and we can confirm that by running an audit check again. Aaaand voila!

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu5.jpg

    6. To automate the process of dependency checking a little bit further, we can even receive alerts from GitHub.

    Publishing npm Package

    Let’s say we are writing our own npm package, and once that is finished, we want to publish it to the global registry. These are some of the best practices that we should follow before doing so.

    1. Avoid publishing secrets to the npm registry → use npm publish --dry-run to review the package before publishing
    2. Protect sensitive data like passwords, tokens etc.
    → use .npmignore and .gitignore to apply Deny list strategy
    → use files property in package.json to apply Allow list strategy

     ⓘ files property takes precedence over ignore file.

    3. Use npm ci for deterministic installations across different environments

    Npm Bonus Points

    Here are some additional best practices that the Vega IT team uses, which may be useful as well.

    1. Malicious packages can take advantage of key lifecycle events (like preinstall, postinstall, preuninstall or postuninstall hooks) when an npm install runs arbitrary commands. In order to prevent that from happening, we can disable run-scripts installation
    → Use npm install --ignore-scripts
    → Add ignore-scripts=true to your .npmrc project file


    2. Assess npm project health
    → Ensure that you are using the latest secure and stable versions with npm outdated

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-pratece-u-tekstu6.jpg

    → Assess environment health with npm doctor which will run a set of checks of your local machine, ensures that you have everything in place to work with npm properly, and even offer some suggestions on what you can do about the possible issues

    how-to-protect-against-vulnerable-and-outdated-components-web-security-blog-series-712-x-283-prateca-u-tekstu.jpg
    Nikola Mitrović Authors Photo
    Nikola Mitrović Software Developer

    Nikola is quite fanatic about web performance and ensuring the best possible User Experience of web apps.