
We rarely write 100% of the code we deploy. Instead, we install packages and with them, a whole lot of trust.
Recent attacks in ecosystems like npm and Rust have made one thing very clear: your backend is only as secure as the packages it depends on.
This post walks through what these attacks look like, how they happen, and what you can do to protect your backend from getting compromised by a single bad npm install.
The Problem: You’re Trusting Too Much Code
Most apps rely on dozens, sometimes hundreds, of third-party packages. Each package pulls in its own dependencies, forming a deep trust tree you rarely inspect. A single malicious package can:
- Steal secrets (API tokens, environment variables, database credentials)
- Inject backdoors or modify source files
- Exfiltrate production data
- Execute arbitrary code during build or deploy
These are not hypotheticals. Imagine deploying a hotfix on Black Friday, only for a dependency to leak your AWS keys.
Real-World Attacks
Past Incidents
- event-stream (2018): A popular npm package was compromised, injecting obfuscated code to steal Bitcoin wallets.
- coa, rc, ua-parser-js (2021): Legitimate npm packages were hijacked to distribute malware.
- Rust Crates (2023): Attackers submitted pull requests to inject malicious code into popular crates.
Recent Threats (2024-2025)
- Polyfill.io (2024): A JavaScript library was compromised via domain takeover, injecting malware into thousands of sites (source).
- npm Typosquatting (2024): The “everything” package mimicked popular libraries, tricking developers into installing malware (source).
- Rust Crates.io Takeovers (2024): Maintainer account compromises led to backdoored crate versions (source).
Why Package Attacks Are Dangerous
- Pre-Deployment Execution: Malicious code can run during install or build.
- CI/CD Exploitation: Attacks target automated pipelines, slipping past manual reviews.
- Hard to Detect: Subtle changes in minor or patch versions (e.g., ^1.0.0) can hide malicious updates.
- Emerging Threats: AI-generated malicious packages evade detection, and containerized environments (e.g., Docker) amplify risks by pulling tainted images.
How to Protect Your Backend
Here are practical, battle-tested defenses to secure your stack.
1. Lock Dependencies
Use lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml) to ensure consistent versions across environments.
npm ci
# NOT npm installLockfiles prevent surprise updates in production.
2. Audit Regularly
Run built-in audits to catch known vulnerabilities.
npm audit
npm audit fixReview changelogs before applying fixes, as npm audit fix --force can introduce breaking or unvetted dependencies.
3. Use Dependency Scanners
Integrate tools into your CI/CD pipelines:
- Snyk: Detects vulnerabilities and suggests fixes.
- Socket.dev: Flags suspicious behavior (e.g., post-install scripts).
- OWASP Dependency-Check: Cross-language vulnerability scanning.
- GitHub Dependabot: Automated alerts for risky dependencies.
Example GitHub Action for Snyk:
name: Snyk Security Scan
on: [push]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}4. Verify Packages Before Use
Before installing:
- Check maintainer count (npm info
). - Review download stats and last update date.
- Skim source code for small utilities.
- Avoid packages with one maintainer or no updates in years.
5. Pin Versions for Critical Systems
Avoid version ranges (e.g., ^1.2.3) in production.
"dependencies": {
"lodash": "4.17.21"
}Note: Pinned versions aren’t immune to yanked or compromised packages, so verify integrity.
6. Exclude Dev Tools in Production
Omit linters, test frameworks, or other dev dependencies from production builds.
# Dockerfile
RUN npm ci --only=production7. Block Post-Install Scripts
Some packages run scripts after install, which can hide malicious code.
npm install --ignore-scriptsUse tools like npm vet (Socket.dev) to detect risky scripts.
8. Generate Software Bill of Materials (SBOM)
Create an SBOM to track dependencies, aiding compliance (e.g., EU’s CRA). Use tools like CycloneDX.
npm install -g @cyclonedx/bom
cyclonedx-bom -o bom.xml9. Multi-Ecosystem Tools
For non-JS/Rust stacks:
- Python: pip-audit
- Java: Maven Dependency Check
- PHP: Composer Security Check
Final Thoughts
Using third-party packages is unavoidable. But so is the risk.
What matters is reducing your exposure
Locking down installs, scanning dependencies, and being skeptical about what you let into your backend.
If your app is in production, treat every package like it could be a potential attack vector. Because one day, it might be.