Acesse a versão em português aqui: Clique Aqui
Hi everyone.
Today I want to share a little more knowledge about a type of attack that has become increasingly frequent: the Supply Chain Attack.
Let's see how this works in the npm ecosystem and what we can do to mitigate this risk.
Remember that I've only used npm as an example here, but this type of attack can occur in other package managers as well!
Here you'll find a tool I developed to mitigate this type of attack. Safeinstall
If you want to see how the tool works, take a look here SafeInstall
Introduction
How many times a day do you run npm install? For most JavaScript and Node.js developers, the answer is: many. This seemingly innocent routine — installing a dependency to solve a problem — hides an attack vector increasingly exploited by cybercriminals: the supply chain attack.
In this article, we explore what these attacks are, how they work in practice using a real demonstration project, what the consequences would be in production environments, and how you can protect yourself.
The project used in the article can be found here: Example Project
What is a Supply Chain Attack?
A supply chain attack occurs when an attacker compromises a component that is part of the software supply chain — something that developers or systems trust and use without question. In the npm ecosystem, this materializes mainly through:
- Malicious packages created from scratch to appear legitimate
- Compromised legitimate packages (abandoned maintenance, hacked account, typosquatting)
-
Lifecycle scripts that execute automatically during
npm install
The critical point is that the developer does not need to do anything beyond installing the package. No need to open a suspicious file, click a link, or run an unknown binary. The simple act of adding a dependency to package.json and running npm install can be enough to compromise the machine, repository, or infrastructure.
Lifecycle Scripts: The Entry Point
npm defines various scripts that run at specific moments in a package's lifecycle:
| Script | When it runs |
|---|---|
preinstall |
Before the package is installed |
install |
During installation |
postinstall |
Immediately after installation — preferred target for attacks |
preuninstall |
Before uninstalling |
postuninstall |
After uninstalling |
prepublish |
Before publishing to the npm registry |
Anyone who runs npm install will execute these scripts automatically, with no clear warning. That is where the danger lies.
Demonstration Project: Structure and Code
To illustrate the attack vector, we built an educational project consisting of:
- "Malicious" package — a package that appears useful but runs code during installation
- Victim project — a project that simply depends on that package
Malicious Package Structure
The package's package.json defines the scripts that will be executed:
{
"name": "utilidades-uteis",
"version": "1.0.0",
"description": "Pacote útil que parece legítimo mas executa código no post-install",
"main": "index.js",
"scripts": {
"postinstall": "node postinstall.js",
"preinstall": "node preinstall.js"
},
"keywords": ["utility", "helper"],
"author": "Atacante Anônimo",
"license": "MIT"
}
Note that postinstall and preinstall point to Node.js scripts. These scripts run automatically during installation.
preinstall.js Script
This script runs before the package is installed:
/**
* PREINSTALL - Runs BEFORE the package is installed
* Another phase where malicious code can execute
*/
console.log('\u001b[35m[preinstall]\u001b[0m This script runs even before the package is installed!');
In a real attack, initial data collection or environment preparation could happen here.
postinstall.js Script — The Heart of the Attack
Here is the script that simulates exfiltration of sensitive data:
const fs = require('fs');
const path = require('path');
const os = require('os');
// Simulates what an attacker COULD collect (only shows, does not send)
const dadosSensiveis = {
executadoEm: new Date().toISOString(),
usuario: os.userInfo().username,
diretorioAtual: process.cwd(),
platform: process.platform,
nodeVersion: process.version,
// A real attacker would try to read:
// env: process.env, // Tokens, passwords, API keys
// arquivos: fs.readdirSync(process.env.HOME)
};
// Creates "proof" file - in real attack would be sent to server
const arquivoProva = path.join(process.cwd(), 'PROVA_ATAQUE_SUPPLY_CHAIN.json');
fs.writeFileSync(arquivoProva, JSON.stringify(dadosSensiveis, null, 2), 'utf8');
console.log(`\u001b[31m[SIMULATED ATTACK]\u001b[0m Data collected saved to: ${arquivoProva}`);
console.log('\nIn a REAL attack, this would be sent to the attacker\'s server.\n');
Example of a file created after the script runs:
And when running the application, the user doesn't even notice what happened:
In a real malicious version, the attacker would replace fs.writeFileSync with an HTTP call (e.g., using https.request) to send this data to a server under their control. The package also exposes a legitimate module (index.js) that does something useful — making the package plausible and reducing suspicion.
Attack Flow
1. Developer: npm install utilidades-uteis
2. npm downloads the package
3. npm runs preinstall → malicious code #1
4. npm runs postinstall → malicious code #2 (collects data)
5. Package installed normally
6. Developer does not realize they have been compromised
Consequences in Real Environments
What could happen if this were a real attack? The consequences vary depending on the victim's context.
1. Exfiltration of Credentials and Secrets
-
Environment variables (
process.env): API tokens (AWS, GitHub, Stripe), database keys, passwords -
.envfiles: credentials in plain text across multiple projects -
Configuration files:
~/.npmrc,~/.aws/credentials,~/.ssh/config
Impact: Access to cloud accounts, databases, private repositories, and third-party systems.
2. Theft of SSH Keys and Certificates
- Reading
~/.ssh/(private keys,known_hosts) - Using the keys to access servers, GitHub, private repositories
Impact: Server intrusion, cloning of private repositories, malicious commits in the victim's name.
3. Cryptojacking
- Running a cryptocurrency miner in the background
- Consuming the victim's machine or server CPU and power
Impact: High infrastructure costs, performance degradation, possible violation of cloud usage policies.
4. Backdoors and Persistence
- Installing remote access tools
- Adding scheduled tasks or startup scripts
Impact: Prolonged control of the machine, espionage, preparation for future attacks.
5. Modification of Other Packages
- Altering code in
node_modulesof other dependencies - Injecting backdoors into libraries used in production
Impact: Compromise at scale, propagation of the attack to all application users.
6. In CI/CD Environments
- Access to pipeline secrets (tokens, credentials)
- Ability to modify build artifacts or Docker images
- Deployment of compromised versions to production
Impact: Compromise of the entire delivery chain, from build to production.
Documented Real-World Cases
| Case | Year | Description |
|---|---|---|
| event-stream | 2018 | Package with ~2M weekly downloads. Malicious code added to steal Bitcoin wallets. |
| ua-parser-js | 2021 | Popular library compromised; ran cryptocurrency miner. |
| coa and rc | 2021 | Typosquatting; packages stole environment variables and sent them to a remote server. |
| node-ipc | 2022 | Added code that modified files on machines of developers from certain geographic regions. |
Protection Measures
-
Use
--ignore-scriptswhen possible:npm install --ignore-scripts -
Audit dependencies:
npm audit,npm audit fix -
Verify scripts:
npm view package-namebefore installing - Specialized tools: Socket.dev, Snyk for detecting suspicious behavior
-
Keep
package-lock.jsonin version control and review changes - Verify package provenance: download counts, active maintenance, open repository
Conclusion
The npm ecosystem is extremely convenient, but that convenience carries risks. The seemingly trivial act of npm install can execute arbitrary code on your machine. Awareness of supply chain attacks and adopting security best practices are essential to reduce the attack surface and protect projects and infrastructure.
The demonstration project is available so you can test the flow in a controlled environment and understand how these attacks work in practice.
This article was written for educational purposes. The demonstration project contains only simulated code and does not perform any real malicious actions.


Top comments (0)