5 ms·
ProTip: use PNPM, not NPM. PNPM 10.x shutdown a lot of these attack vectors. 1. Does not default to running post-install scripts (must manually approve each)
by twistedpair 1y ago
ProTip: use PNPM, not NPM.
PNPM 10.x shutdown a lot of these attack vectors.
1. Does not default to running post-install scripts (must manually approve each)
2. Let's you set a min age for new releases before `pnpm install` will pull them in - e.g. 4 days - so publishers have time to cleanup.
NPM is too insecure for production CLI usage.
And of course make a very limited scope publisher key, bind it to specific packages (e.g. workflow A can only publish pkg A), and IP bound it to your self hosted CI/CD runners. No one should have publish keys on their local, and even if they got the publish keys, they couldn't publish from local.
(Granted, GHA fans can use OIDC Trusted Publishers as well, but tokens done well are just as secure)
- latchkey 1y agoProTip: `use bun` Funny that this is getting downvoted, but it installs dependencies super fast, and has the same approval feature as pnmp, all in a simple binary.
- benjifri 1y agoThis is like saying "use MacOS and you won't get viruses" in the 2000s
- hiccuphippo 1y agoExcept trying it out takes a minute and costs nothing.
- latchkey 1y agoThe suggestion was to use pnpm, and I'm suggesting something I prefer more than pnpm.
- koito17 1y agoBun disables post-install scripts by default and one can explicitly opt-in to trusting dependencies in the package.json file. One can also delay installing updated dependencies through keys like `minimumReleaseAge`. Bun is a drop-in replacement for the npm CLI and, unlike pnpm, has goals beyond performance and storage efficiency. Not sure what your analogy is trying to imply.
- salomonk_mur 1y agoWhich was for the most part true.
- hexasquid 1y ago"Rewrite it in rust"
- tripplyons 1y agoIs there a way to set a minimum release age globally for my pnpm installation? I was only able to find a way to set it for each individual project.
- Operyl 1y agoDid you try putting it in your global config file? Windows: ~/AppData/Local/pnpm/config/rc macOS: ~/Library/Preferences/pnpm/rc Linux: ~/.config/pnpm/rc
- twistedpair 1y agoI think it's a `pnpm-workspace.yaml` setting, for now, but PNPM has been pretty aggressive with expanding this feature set [1]. [1] https://pnpm.io/supply-chain-security https://pnpm.io/supply-chain-security
- embedding-shape 1y ago> NPM is too insecure for production CLI usage. NPM was never "too insecure" and remains not "too insecure" today. This is not an issue with npm, JavaScript, NodeJS, the NodeJS foundation or anything else but the consumer of these libraries pulling in code from 3rd parties and pushing it to production environments without a single review. How this still fly today, and have been since the inception of public "easy to publish" repositories remains a mystery to me even today. If you're maintaining a platform like Zapier, which gets hacked because none of your software engineers actually review the code that ends up in your production environment (yes, that includes 3rd party dependencies, no matter where they come from), I'm not sure you even have any business writing software. The internet been a hostile place for so long, that most of us "web masters" are used to it today. Yet it seems developers of all ages fall into the "what's the worst that can happen?" trap when pulling in either one dependency with 10K LoC without any review, or 1000s of dependencies with 10 lines each. Until you fix your processes and workflows, this will continue to happen, even if you use pnpm. You NEED to be responsible for the code you ship, regardless of who wrote it.
- jama211 1y ago“Personally, I never wear a seatbelt because all drivers on the road should just follow the road rules instead and drive carefully.” I don’t control all the drivers on the road, and a company can’t magically turn all employees into perfect developers. Get off your high horse and accept practical solutions.
- embedding-shape 1y ago> and a company can’t magically turn all employees into perfect developers Sure, agree, that's why professionals have processes and workflows, everyone working together to build the greatest stuff you can. But when not a single person in the entire company reviews the code that gets deployed and run by users, you have to start asking what kind of culture the company has, it's borderline irresponsible I'd say.
- jama211 1y ago
- tetha 1y ago> And of course make a very limited scope publisher key, bind it to specific packages (e.g. workflow A can only publish pkg A), and IP bound it to your self hosted CI/CD runners. No one should have publish keys on their local, and even if they got the publish keys, they couldn't publish from local. I've by now grown to like Hashicorp Vaults/OpenBao's dynamic secret management for this. It's a bit complicated to understand and get to work at first, but it's powerful: You mirror/model the lifetime of a secret user as a lease. For example, a nomad allocation/kubernetes pod gets a lease when it is started and the lease gets revoked immediately after it is stopped. We're kinda discussing if we could have this in CI as well - create a lease for a build, destroy the lease once the build is over. This also supports ttl, ttl-refreshes and enforced max-ttls for leases. With that in place, you can tie dynamically issued secrets to this lease and the secrets are revoked as soon as the lease is terminated or expires. This has confused developers with questionable practices a lot. You can print database credentials in your production job, run that into a local database client, but as soon as you deploy a new version, those secrets are deleted. It also gives you automated, forced database credential rotation for free through the max_ttl, including a full audit log of all credential accesses and refreshes. I know that would be a lot of infrastructure for a FOSS project by Bob from Novi Zagreb. But with some plugin-work, for a company, it should be possible to hide long-term access credentials in Vault and supply CI builds with dropped, enforced, short-lived tokens only. As much as I hate running after these attacks, they are spurring interesting security discussions at work, which can create actual security -- not just checkbox-theatre.
- mikestorrent 1y agoDefinitely curious if you've come up with a way to give each build a short lived vault approle somehow in any CI system.
- LelouBil 1y agoI would love to use this (for homelab stuff currently) but I would love a way to have vault/openbao be fully configuration-as-code and version controlled, and only have the actual secret values (those that would not be dynamic) in persistent storage.
- acedTrex 1y ago
- blktiger 1y agoBoth NPM and Yarn have a way to disable install scripts which everyone should do if at all possible.
- twistedpair 1y agoGood point, but until many popular packages stop requiring install.sh to operate, you'll still need to allowlist some of them. That is built into the PNPM tooling, luckily :)
- benoau 1y agoOr just 'npm ci' so you install exactly what's in your package-lock.json instead of the latest version bumps of those packages. This "automatic updating" is a big factor in why these attacks are working in the first place. Make package updating deliberate instead of instant or on an arbitrary lag.
- dachris 1y agoYou'd be surprised how many people run 'npm i' in their CI. I've seen this on multiple occasions. 'npm ci' is some mitigation, but doesn't protect against getting hit when running 'npm i(nstall)' during development.
- cluckindan 1y agoUpdate your knowledge. ”npm install” hasn’t done auto-upgrades for years now.
- cluckindan 1y ago”npm install” does not auto-upgrade packages, it installs them according to the lockfile. It didn’t always work like that, but it has worked like that for many years.
- halflife 1y agoWhat does it do with packages that download binaries for specific architecture in the post script?
- madeofpalk 1y agoYou don't need post-install scripts for this. Use optionalDependencies instead https://github.com/nrwl/nx/blob/master/packages/nx/package.json#L88-L99 https://github.com/nrwl/nx/blob/master/packages/nx/package.j... Each of those deps contains a constraint installing only for the relevant platform.
- halflife 1y agoThat’s cool, now I wish all libraries that need binaries would opt to use that instead of post script
- zahlman 1y agoDo keep in mind that the binaries are still binaries. Even if your installation process doesn't run any untrusted code from the package, you can't audit the binaries like you might the .js files prior to first run.
- mook 1y agoAs far as I can understand from the documentation, that doesn't actually specify in that config that one of them is required, does it? That is, if they _all_ fail to install as far as the system is concerned there's nothing wrong? There will be runtime errors of course, but that's sort of disappointing…
- edoceo 1y agoAs stated, you manually approve them.
- hinkley 1y agoNpm is what happens when you let tech debt stack up for years too far. It took them five attempts to get lock files to actually behave the way lock files are supposed to behave (lockfile version 3, + at least 2 unversioned attempts before that). It’s clear from the structure and commit history they’ve been working their asses off to make it better, but when you’re standing at the bottom of a well of suck it takes that much work just to see daylight. The last time I chimed in on this I hypothesized that there must have been a change in management on the npm team but someone countered that several of the maintainers were the originals. So I’m not sure what sort of Come to Jesus they had to realize their giant pile of sins needed some redemption but they’re trying. There’s just too much stupid there to make it easy. I’m pretty sure it still cannot detect premature EOF during the file transfer. It keeps the incomplete file in the cache where the sha hash fails until you wipe your entire cache. Which means people with shit internet connections and large projects basically waste hours several times a week doing updates that fail.
- andrewmcwatters 1y ago[dead]
- ornornor 1y ago> cannot detect premature EOF during the file transfer. It keeps the incomplete file in the cache where the sha hash fails until you wipe your entire cache. I wonder what circumstances led to saying “this is okay we’ll ship it like that”
- cyanydeez 1y agoIgnorance. Most programmers in open source operate on the "works on my machine"
- xp84 1y agoTrue, and things that manifest only on old/slow hardware or on bad internet are the worst kind for this, since 100% of developers who have any say in the matter would never accept such circumstances at all, so they’re always approaching every issue with multi-gigabit speeds, zero latency, and this year’s $3,000 Mac. “What do you mean the page loads slowly?”
- madeofpalk 1y agoYou shouldn't have any keys anywhere at all. Use OIDC https://docs.npmjs.com/trusted-publishers https://docs.npmjs.com/trusted-publishers Unfortunately you need to `npm login` with username and password in order to publish the very first version of a package to set up OIDC.
- c-hendricks 1y agoA whole single supported CI partner outside their own corporate family. They really planned this out well.
- twistedpair 1y agoI'm struggling to understand why Trusted Publishers is any better. Let's say you have a limited life, package specific scoped, IP CIDR bound publishing key, running on a private GH workflow runner. That key only exists in a trusted clouds secret store (e.g. no one will have access it from their laptop). Now let's say you're a "trusted" publisher, running on a specific GitHub workflow, and GitHub Org, that has been configured with OIDC on the NPM side. By virtue of simply existing in that workflow, you're now a NPM publisher (run any publish commands you like). No need to have a secret passed into your workflow scope. If someone is taking over GitHub CI/CD workflows by running `npm i` at the start of their workflow, how does the "Trusted Publisher" find themselves any more secure than the secure, very limited scope token?
- dzonga 1y agopnpm on the backend, frontend use nobuild.
- jjice 1y agoThere were some recent posts I saw about "dependency cooldowns", which seem to be what you're referring to in item 2. The idea really resonated with me. That said, I hard pin all our dependencies and get dependabot alerts and then look into updates manually. Not sure if I'm a rube or if that's good practice.
- jaapz 1y agoThat's good practice. God knows how many times I've been bitten by npm packages breaking on minor or even patch version changes, even when proudly proclaiming to use semver
- poetril 1y agoHow does bun compare? Does it have similar features as well?
- flanbiscuit 1y agoyes bun does both of the things mentioned in the parent comment: > Unlike other npm clients, Bun does not execute arbitrary lifecycle scripts like postinstall for installed dependencies. Executing arbitrary scripts represents a potential security risk. https://bun.com/docs/pm/cli/install#lifecycle-scripts https://bun.com/docs/pm/cli/install#lifecycle-scripts > To protect against supply chain attacks where malicious packages are quickly published, you can configure a minimum age requirement for npm packages. Package versions published more recently than the specified threshold (in seconds) will be filtered out during installation. https://bun.com/docs/pm/cli/install#minimum-release-age https://bun.com/docs/pm/cli/install#minimum-release-age
- dschofie 1y agoReading through the post it looks like this infects via preinstall? > The new versions of these packages published to the NPM registry falsely purported to introduce the Bun runtime, adding the script preinstall: node setup_bun.js along with an obfuscated bun_environment.js file.
- twistedpair 1y agoYou're right. PNPM disables all install scripts by default. I was just noting one example.
- zahlman 1y agoFor Python ecosystem people: > Does not default to running post-install scripts (must manually approve each) To get equivalent protection, use `--only-binary=:all:` when running `pip install` (or `uv pip install`). This prevents installing source distributions entirely, using exclusively pre-built wheels. (Note that this may limit version ability or even make your installation impossible.) Python source packages are built by following instructions provided with the package (specifying a build system which may then in turn be configured in an idiosyncratic way; the default Setuptools is configured using a Python script). As such, they effectively run a post-install script. (For PAPER, long-term I intend to design a radically different UI, where you can choose a named "source" for each package or use the default; and sources are described in config files that explain the entire strategy for whether to use source packages, which indexes to check etc.) > Let's you set a min age for new releases before `pnpm install` will pull them in - e.g. 4 days - so publishers have time to cleanup. Pip does not support this; with uv, use `--exclude-newer`. This appears to require a timestamp; so if you always want things up to X days old you'll have to recalculate.
- acdha 1y ago> Pip does not support this; with uv, use `--exclude-newer`. This appears to require a timestamp; so if you always want things up to X days old you'll have to recalculate. I do this by having my shell init do this: export UV_EXCLUDE_NEWER=$(date -Iu -d "14 days ago") That’s easy to override if you need to but otherwise seamless.
- zahlman 1y agoFWIW, I'd like if these tools had an option to prefer the oldest version satisfying the given constraints (rather than the newest, as it is now — probably still a better default).
- collinmanderson 1y ago> prefer the oldest version satisfying the given constraints The problem is there's no metadata for which versions fix security bugs, and therefore which previous versions are now insecure.
- lrvick 1y agoPnpm cannot be built without an existing pnpm binary meaning there is no way to bootstrap it from audited source code. Perfect trusting trust attack situation. Full source bootstrapped NPM with manually reviewed dependencies is the only reasonably secure way to use NodeJS right now.