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.

bash
npm ci
# NOT npm install

Lockfiles prevent surprise updates in production.

2. Audit Regularly

Run built-in audits to catch known vulnerabilities.

bash
npm audit
npm audit fix

Review 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:

text
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.

json
"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
# Dockerfile
RUN npm ci --only=production

7. Block Post-Install Scripts

Some packages run scripts after install, which can hide malicious code.

bash
npm install --ignore-scripts

Use 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.

bash
npm install -g @cyclonedx/bom
cyclonedx-bom -o bom.xml

9. 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.