← Back to Blog
· 5 min read · API Stronghold Team

npm postinstall Scripts Can Read Your .env. Most Projects Let Them.

Cover image for npm postinstall Scripts Can Read Your .env. Most Projects Let Them.

On April 3rd, a package called strapi-plugin-events appeared on npm at version 3.6.8. It looked like a community plugin. Version 3.6.8 implies a mature project with a long history. Three files, no unusual dependencies, named exactly like a real Strapi plugin would be.

It was malware. The postinstall script read your .env file and sent it to an external server.

Same Playbook, Different Package

Two weeks ago, the Axios maintainer account compromise let attackers publish a malicious version of one of the most-downloaded packages in the Node ecosystem. The attack vector: a postinstall hook that exfiltrated environment variables.

Now strapi-plugin-events. Same technique. A package that blends into a specific ecosystem’s naming conventions, a high-looking version number to project legitimacy, and a postinstall script that does the actual damage.

This isn’t a coincidence. It’s a pattern. Someone is testing how well this vector works across different target audiences. Axios targets anyone using HTTP clients. Strapi plugins target teams running headless CMS backends, which often means database credentials, Cloudinary keys, Stripe secrets, and provider API tokens all sitting in the same .env file.

The disguise is cheap to build. Find a plausible package name, pick a version number that looks like the project has been around for years, write three files that import something innocuous, and add one malicious postinstall script. Getting it past casual inspection takes maybe an hour.

What Happens in the Three Seconds After npm install

The postinstall hook runs automatically, before your code does anything, before your linter runs, before your tests run. By the time you see your terminal prompt again, the damage is done.

In the Axios incident, researchers estimated the malicious version was live for roughly three hours before it was yanked. Three hours sounds short. But that’s three hours in which your credentials left the machine. The attacker doesn’t need to act immediately. They can store the harvest and work through it later.

Long-lived API keys don’t expire. An attacker who gets your SendGrid API key today has it forever, or until you manually rotate it. If you don’t notice the compromise for a week, they’ve had a week. If you never notice, they have permanent access.

“Rotating after you find out” is the standard advice, and it’s correct. But it assumes you find out. Most teams didn’t know the Axios package was compromised until security researchers posted about it publicly. If you’d installed the malicious version during that three-hour window, you might have rotated your npm token (if you were paying attention) and completely missed that your database URL, your payment processor key, and your cloud storage credentials also went out the door.

The Math on Short-Lived Credentials

Here’s what changes if your credentials expire in 15 minutes.

The attacker still gets them. The postinstall hook still runs, the .env still gets sent. Nothing about that changes. But when they go to use those credentials, they’re expired. The harvest is useless.

This is the core argument for short-lived scoped tokens. Not that they prevent exfiltration. They don’t. They make exfiltration worthless.

A phantom token that’s scoped to a single operation and expires in 15 minutes gives an attacker a 15-minute window to act on what they stole. In practice, that’s not enough time to pivot. They need to identify the service, figure out what the token can access, and do something meaningful with it. The window closes before they can work.

Long-lived keys flip this equation entirely. The attacker has no time pressure at all. They can harvest credentials at scale, store them, and work through them methodically weeks or months later.

Make exfiltrated credentials worthless

Short-lived scoped tokens from API Stronghold expire in minutes. A compromised postinstall hook gets credentials that are already dead by the time an attacker tries to use them.

No credit card required

What You Can Actually Do Right Now

Audit your postinstall scripts. Run this in any project:

npm ls --parseable | xargs -I {} sh -c 'cat {}/package.json 2>/dev/null' | grep -A2 '"postinstall"'

You’re looking for postinstall scripts in packages you didn’t write. Most legitimate packages don’t have them. When you see one, read it.

Use npm ci instead of npm install in CI/CD. npm ci installs from the lockfile exactly. It will refuse to install if there’s a mismatch between package.json and package-lock.json. This doesn’t prevent a compromised package that’s already in your lockfile, but it does prevent drift and makes your dependency graph more auditable.

Check npm audit results before dismissing them. A lot of teams have audit warnings set to non-blocking because of false positives. When a genuinely malicious package gets flagged, it looks like noise.

Scope your .env variables. If your postinstall hook can only exfiltrate what’s in .env, minimizing what lives there limits the blast radius. Credentials that aren’t loaded as environment variables can’t be grabbed this way.

Move toward short-lived credentials where the service supports it. AWS IAM has temporary credentials via STS. Many OAuth providers support short token lifetimes. If you’re managing your own API keys with something like API Stronghold’s phantom tokens, you can set expiry windows that make harvesting irrelevant.

The Structural Problem

The npm ecosystem does not verify that a package does what it says it does. Version numbers are author-assigned with no external validation. Package names can mimic real projects closely enough to fool a distracted engineer running npm install strapi-plugin-events without double-checking the download count or author.

This isn’t going to be fixed at the registry level anytime soon. The controls available to you are: watch what you install, audit what postinstall scripts run, and make sure that even if something gets your credentials, those credentials don’t work for long.

Supply chain attacks are getting more frequent and more targeted. The defense isn’t perfect hygiene at the install step. Engineers make mistakes, especially under time pressure. The defense is credentials that expire fast enough that a mistake at the install step doesn’t become a permanent breach.

Your .env file is the attack surface: stop treating it like a secret store.

API Stronghold injects short-lived scoped tokens at runtime so your .env never holds live credentials. Postinstall hooks grab nothing useful.

No credit card required

Keep your API keys out of agent context

One vault for all your credentials. Scoped tokens, runtime injection, instant revocation. Free for 14 days, no credit card required.

Get posts like this in your inbox

AI agent security, secrets management, and credential leaks. One email per week, no fluff.

Your CI pipeline has permanent keys sitting in env vars right now. Scoped, expiring tokens fix that in an afternoon.

One vault for all your API keys

Zero-knowledge encryption. One-click sync to Vercel, GitHub, and AWS. Set up in 5 minutes — no credit card required.