


























A common supply chain security issue with npm packages is the capability of malicious or compromised npm packages to execute arbitrary code or OS commands during the package installation process. This has been a common vector and security concern for developers in the JavaScript ecosystem for many years now and has been exploited by attackers to compromise systems.
The npm package manager (the CLI, not the registry) has a feature called lifecycle hooks. These hooks are scripts that are defined in the package.json manifest file and are executed at different stages of your projects installation process. One of the most commonly used lifecycle hooks is the postinstall script (but there’s also preinstall for example). This script is executed after a package is installed and can be used to perform additional setup or configuration tasks.
So to be practical, imagine the following scenario: your project depends on some third-party packages from npm, right? if you install a package example-dummy-package and this package itself defines in its own package.json file a postinstall run-script hook, then that command will run when you run the following in your project:
npm install `example-dummy-package`
This behavior applies to both direct dependencies or any transitive dependencies in your dependency graph that may define a postinstall (or other lifecycle run-script hook).
As you can already imagine by now, the significant risk of the npm package manager allowing third-party dependencies to execute random commands means that any maintainer and any package in your dependency tree may run any operating system commands during your own npm install process.
While the risk is somewhat contained because the command would still only run with the privileges of your own user (or the CI user) that runs the npm install command, it is still significant.
There are countless examples of npm packages that were published to the npm registry with malicious intents and having postinstall scripts that would run malicious commands on the user’s machine. Here are a few examples:
postinstall script that would steal the user’s npm credentials. This was indeed part of the official ESLint project that you probably heard of already. One of the package authors was compromised and the attacker published a new version of the package with the malicious script.postinstall script that would run a command to steal the user’s environment variables. Note, this package was a typo-squatting attack that was meant to look like the popular cross-env package from the popular JavaScript developer Kent C. Dodds.Specifically, the case of crossenv shows the significant and security risk of postinstall scripts. Meaning, even if the system user used to install npm packages isn’t the root user, it still has access to sensitive information such as environment variables, .env files, and other secrets that could be used to breach the system and would be easily exfiltrated by a malicious postinstall script.
ignore-scripts flag?Ok, so how do we fix this postinstall scripts problem?
The npm CLI has a flag called ignore-scripts that can be used to prevent the execution of any lifecycle hooks defined in the package.json file of the packages you are installing. This flag can be used with any npm command that installs packages, such as npm install, npm ci, npm update, etc.
Here’s how you can use the ignore-scripts flag:
npm install --ignore-scripts <package-name>
This will install the package you specify without running any lifecycle hooks defined in the package’s package.json file of that package or any of its own transitive dependencies.
Yarn uses the enableScripts configuration flag to control whether lifecycle scripts are executed during package installation. By default, this flag is set to true which means that lifecycle scripts are executed. You can set this flag to false to disable the execution of lifecycle scripts during package installation.
The enableScripts configuration is set in the .yarnrc.yml file.
ignore-scripts flag by defaultBetter than passing the --ignore-scripts flag every time you install a package, you can configure npm to always ignore scripts by default. You can do this by setting the ignore-scripts configuration option in your .npmrc file:
echo "ignore-scripts=true" >> .npmrc
This is useful because it saves you from having to remember to pass the --ignore-scripts flag every time you install an npm package. It also helps to enforce the best practice of always ignoring scripts when installing packages from npm.
Bonus, if you push this .npmrc file to your project’s repository, you can enforce this best practice across your team and CI/CD pipelines so no one else needs to be bothered with remembering to pass the --ignore-scripts flag either.
To take it further, I also recommend applying the ignore-scripts=true configuration option for npm on the system level. This way, you can ensure that it is the default across all projects you use or clone on your system.
This is how you configure npm to ignore scripts by default on the system level:
npm config set ignore-scripts true
ignore-scriptsSo you should be aware that some third-party packages on npm may require their postinstall scripts to run in order to function correctly.
Some people will argue:
But what if you depend on legitimate packages that use postinstall scripts? 🤷
For example, a package may use a postinstall script to compile native addons, or to set up configuration files, or to perform other necessary setup tasks. A few examples are these packages:
Meaning, if you install any of these packages and you default to not allowing post-install scripts then these packages may not install correctly or may not work as expected.
So should you ignore scripts by default or not?
It’s a trade-off between security and functionality. My opinionated recommendation is to always use ignore-scripts by default and only disable it on a case-by-case basis when you trust the package maintainer and you know what the postinstall script does.
If the third-party package requires postinstall scripts to run and is a regular part of the project, you can use LavaMoat’s allow-script package to manage an allow-list of trusted packages.
Another reason to consider disabling postinstall by default and prioritizing security is due to a prior academic research study that showed that about 2% of the npm registry actually has postinstall scripts defined. This means that the majority of packages you install won’t be affected by this change.
“we found 2.2% (33,249) of packages use install scripts, indicat- ing that 97.8% of packages may follow npm recommendation of not using the install script as best security practices.”
postinstall scriptsIf you want to audit your project’s configuration and security posture for postinstall scripts, you can use a dummy test package made available by the LavaMoat project maintainers called @lavamoat/preinstall-always-fail
npm install @lavamoat/preinstall-always-fail
If this package fails to install then it means that your project is configured to allow postinstall scripts (not a good sign of security). If it installs successfully then it means that your project is configured to ignore postinstall scripts (a good sign of security).
ignore-scriptsHere are some best practices for using the ignore-scripts flag with npm:
ignore-scripts by default: Always use the ignore-scripts flag when installing packages from npm. This will help mitigate the risk of malicious packages executing arbitrary commands on your system.ignore-scripts in CI/CD pipelines: You need to install dependencies in your CI/CD pipelines so make sure to always use the ignore-scripts flag to prevent any malicious packages from executing arbitrary commands on your CI/CD system (and potentially stealing environment variables, spinning up cloud infra and using it for crypto mining, etc).ignore-scripts in your project, you can use the npq tool which is a wrapper around the npm CLI that will automatically run heuristics to collect security information about the packages you are installing and will automatically warn you if a package has a postinstall script.此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。