12 ms·
You too can run malware from NPM (I mean without consequences)
- cluckindan 1y agoLavaMoat looks great on paper, but not supporting Webpack HMR is a dealbreaker.
- naugtur 1y agoYou're using HMR in your app's production bundle? How?
- naugtur 1y agoIf you mean during development - you can opt out of using lavamoat in development for your webpack bundle (I'm assuming you're not running your untested code on valuable data)
- cluckindan 1y agoWell, that’s not exactly reassuring. Having a very different runtime environment in production is grounds for hard to debug issues. Is it possible to generate the allowlist at development time without having the webpack plugin loaded? If it’s only generated at build time, it won’t protect against malicious packages getting installed in CI just before the build happens.
- naugtur 1y agoYou need to juggle two builds - one while you're iterating rapidly and another when you're near start and finish of the increment. Not a lot of work compared to auditing a thousand packages. Try it and see. There's tradeoffs but if you roll it out, it is very powerful.
- mohsen1 1y agonpm should take responsibility and up their game here. It’s possible to analyze the code and mark it as suspicious and delay the publish for stuff like this. It should prevent publishing code like this even if I have a gun to my head
- sesm 1y agoI think malware check should be opt-in for package authors, but provide some kind of 'verified' badge to the package. Edit: typo
- naugtur 1y agonpm is on life support by msft. But there's socket.dev that can tell you if a package is malicious within hours of it being published.
- shreddit 1y ago“within hours” is at least one hour too late, and most likely multiple hours.
- naugtur 1y agoAbsolutely not. you get npm packages by pulling not them pushing them to you as soon as a new version exist. The likelyhood of you updating instantly is close to zero and if not, you should set your stuff up so that it is. Many ways to do that. Even better if compared to a month or two - which is how long it often takes for a researcher to find a carefully planted malware. Anyway, the case where reactive tools (detections, warnings) don't catch it is why LavaMoat exists. It prevents whole classes of malware from working at runtime. The article (and repo) demonstrates that.
- rs186 1y agoSure, it should never happen in CI environment. But I bet that every second, someone in the world is running "npm install" to bring in a new dependency to a new/existing project, and the impact of a malicious release can be broad very quickly. Vibe coding is not going to slow this down.
- clbrmbr 1y agoHow much money have the attackers stolen so far? Has someone done an analysis of the blockchains for the destination addresses?
- naugtur 1y agoclick through to the article, it has a link to a view that lists the laughable profit
- nodesocket 1y agoI'm actually shocked they have not stolen more seeing the breach impact radius? Perhaps we can thank wallets and exchanges for blacklisting the addresses and showing huge warnings like the one shown in the article.
- shreddit 1y agoIt was discovered pretty quickly, i don’t think most “big” projects update their packages within minutes of publication.
- pixl97 1y agoReally I'd say the key here is timing. I didn't look into what time the NPM packages were updated, but there are a few key times depending on what markets you're targeting. If it were Indian devs it would be around 2AM CST and if it's US devs it would be around 10AM CST. This is when I see the ramp up in queuing in CI/CD builds that lasts a few hours across companies and is more likely to trigger a package getting rebuilt.
- zachrip 1y agoIt was also packages that in my experience don't often find themselves on the frontend.
- naugtur 1y ago
- p2detar 1y agoI’ve been out of the loop with npm for a while, but are there still no package namespaces?
- uallo 1y agohttps://docs.npmjs.com/about-scopes https://docs.npmjs.com/about-scopes
- diggan 1y agoNamespaces have existed since ~2016 at least in npm, but since it's not enforced and people want "nice looking" package names, the ecosystem still hasn't fully embraced it. It seems like more and more projects are using them (probably because all "good" names are already taken), but probably way less than half of all popular packages are scoped/namespaced.
- keysdev 1y agoWell there is jsr now....
- stby 1y agoI am also out of the loop here, how would namespaces have helped?
- clbrmbr 1y agoIs it typical in the JS space to include dependencies without versioning? Also, curious: does freezing a version really provide much protection? Shouldn’t a commit hash be used? (Attacker can change a tag.)
- naugtur 1y agopackages published to npm are immutable. if you pin a version, you get the same exact version as long as MSFT servers are not compromised. Installing from git is not recommended and has more issues than you might think https://dev.to/naugtur/a-phish-on-a-fork-no-chips-52cc https://dev.to/naugtur/a-phish-on-a-fork-no-chips-52cc You are supposed to update packages, even if you use lockfiles (very common) or tools that pin your direct dependencies (renovate etc. not so common) And when you do update, will you read the package and all of its updated dependencies? It's a hard problem with a bunch of tradeoffs. Can be done, with enough attention and tools. Tools include LavaMoat :)
- clbrmbr 1y agoRe: updates: I was just thinking of waiting a few weeks on the updates to allow compromised packages to be discovered.
- naugtur 1y agosocket.dev will find most malware within hours of it being published. with LavaMoat most malware won't work even if you don't detect it.
- whilenot-dev 1y ago> packages published to npm are immutable. Depends how you'd refer to them... tags ("@latest", "@next" etc.) are not immutable and it's best to rely on the checksums in the lock file.
- vel0city 1y agoThe package-lock.json includes a hash of the package, not just a version number which should be immutable.
- herpdyderp 1y agoI’m intrigued but is that compartmentalization not incredibly expensive?
- naugtur 1y agoIt's within the same process and realm (window) It has a cost, but it's nothing compared to putting every dependency of a large app in a separate iframe/process and figure out a way for them to communicate.
- cluckindan 1y agoHave you tried to find ways to break it? Plenty of objects in the browser API contain references to things that could be used to defeat the compartmentalization. If one were to enumerate all properties on window and document, how many would be objects with a reference back to window, document or some API not on the allowed list?
- cowbertvonmoo 1y agoI maintain ses, the compartment primitive LavaMoat relies on. The ses shim for hardenedjs.org creates compartments that deny guest code the ability to inspect the true global object or lexically reference any of its properties. By default, each compartment only sees the transitively frozen intrinsics like Array and Object, and no way to reach the genuine evaluators. The compartment traps the module loader as well, so you can only import modules that are explicitly injected. That leaves a lot of room for the platform to make mistakes and endow the compartment with gadgets, but also gives us a place to stand to mount a defense that is not otherwise prohibitively expensive.
- CyberMacGyver 1y agoLooks like OP is one of the contributors to LavaMoat
- naugtur 1y agoYes, I am. I came up with the first successful attempt at integrating the Principle of Least Authority software in LavaMoat with Webpack and wrote the LavaMoat Webpack Plugin. Also, together with a bunch of great folks at TC39 we're trying to get enough building blocks for the same-realm isolation primitives into the language. see hardenedjs.org too I'm doing the rounds promoting the project today because at this point all we need to eliminate certain types of malware is get LavaMoat a lot more adoption in the ecosystem. ( and that'll give me bug reports and maybe even contributions? :) )
- hn92726819 1y agoI think most people are fine with promoting a cool project you work on, but it's best practice to disclose that in the article. Even something like "If your project was set up with LavaMoat (a project I've been working on), ..." would be enough. I think that's why they made the comment.
- btown 1y agoI'm often curious about how effective runtime quasi-sandboxing is in practice (at least until support at the TC39 level lands). My understanding is that if you can run with a CSP that prevents unsafe-eval, and you lock a utility package down to not be able to access the `window` object, you can prevent it from messing with, say, window.fetch. But what about a package that does assume the existence of window or globalThis? Say, a great many packages bridging non-React components into the React ecosystem. Once a package needs even read-only access to `window`, how do you protect against supply-chain attacks on that package? Even if you read-only proxy that object, for instance, can you ensure that nothing in `window` itself holds a reference to the non-proxied `window`? Don't get me wrong - this project is tremendously useful as defense-in-depth. But curious about how much of a barrier it creates in practice against a determined attacker.
- jefozabuss 1y agoSeems like people already forgot about Jia Tan. By the way why doesn't npm have already a system in place to flag sketchy releases where most of the code looks normal and there is a newly added obfuscated code with hexadecimal variable names and array lookups for execution...
- tom1337 1y agoIt would also be great if a release needs to be approved by the maintainer via a second factor or an E-Mail verification. Once a release has been published to npm, you have an hour to verify it by clicking a link in an email and then enter another 2FA (separate OTP than for login, Passkey, Yubikey whatever). That would also prevent publishing with lost access keys. If you do not verify the release within the first hour it gets deleted and never published.
- naugtur 1y agoThat's why we never went with using keys in CI for publishing. Local machine publishing requires a 2fa. automated publishing should use something like Pagerduty to signal that a version is being published to a group of maintainers and it requires an approval to go through. And any one of them can veto within 5 minutes. But we don't have that, so gotta be careful and prepare for the worst (use LavaMoat for that)
- Cthulhu_ 1y agoNot through e-mail links though, that's what caused this in the first place. E-mail notification, sure, but they should also do a phishing training mail - make it legit, but if people press the link they need to be told that NPM will never send them an email with a link.
- dist-epoch 1y ago> flag sketchy releases Because the malware writers will keep tweaking the code until it passes that check, just like virus writers submit their viruses to VirusTotal until they are undetected.
- j45 1y agoHow does one avoid malware in npm specifically? Makes me not want to use the ecosystem, which isn’t always possible.
- beardyw 1y ago>Makes me not want to use the ecosystem I came to that conclusion long ago.
- pimterry 1y agoThis attack is pretty bad, but as shown by the tiny ROI for the attacker mentioned in this article (about $500 so far: https://intel.arkm.com/explorer/entity/61fbc095-f19b-479d-a037-5469aba332ab https://intel.arkm.com/explorer/entity/61fbc095-f19b-479d-a0...) this really isn't quite as ecosystem-catastrophic as it sounds, for a few reasons: * Major attacks on large packages like this are caught fairly quickly - a few hours in this case - making the vulnerable window _relatively_ small. * NPM locks installed dependencies by default, against both the version & a hash of the content, so you'll only install the new malicious version if you happen to be adding or updating this dependency specifically within the window this version is still live. It's effectively sort-of TOFU. If even you ran `npm install` in a project already using this dependency in the specific window it was live, you will not normally install the malicious version. * There's quite a few tools to help mitigate the risk here, like https://socket.dev https://socket.dev and npq (https://github.com/lirantal/npq https://github.com/lirantal/npq). As one datapoint, look at the download stats for the affected Chalk package for example (https://www.npmjs.com/package/chalk?activeTab=versions https://www.npmjs.com/package/chalk?activeTab=versions) - the vast majority of installs were not installing the latest version anyway. There are caveats to this: e.g. you can use npm without a lockfile, in which case a fresh local install can pull down unexpected versions, or you could be manually updating/adding a different package which happens to depend on an affected package (which might trigger a lockfile update, which might then fetch the latest version of the subdependency) during the vulnerable window, or of course it's totally possible you might install the package for the first time at the precisely wrong moment, etc etc. This is definitely bad, and could have been extremely disastrous if it wasn't caught. But in practice, npm & the ecosystem have put in quite a few protections that do help to _mostly_ mitigate these kind of risks in typical use cases (but not completely, and there's definitely plenty more work to do!) and it's certainly not the case that millions of JS developers & projects were all catastrophically pwned today.
- AtNightWeCode 1y agoI think JS should be all source and no packages at all.
- phil294 1y agoWhat about complex SPAs? Database drivers? Polyfills? TypeScript?
- AtNightWeCode 1y agoPulling the source and compiling the package instead of pulling the package. Not much difference. Maybe slower build times but more secure and better builds.
- k4rnaj1k 1y ago[dead]
- deleted 1y ago[deleted]
- erpderp 1y agoIn the example snippets from OP, the code shown is in the browser. I'm failing to see how the interception, as described, couldn't be handled by a decent Content Security Policy - instead of requiring yet another npm package. Seems safer than installing another package to address risk from ... installing packages.
- ghrl 1y agoI suppose if you're using a bundler, you will ship JS bundles including the malicious packages from your own trusted domain. How could CSP prevent this or similar attacks?
- erpderp 1y agoAccording to the OP, in this specific case, the malware was mostly just intercepting legitimate fetch(), etc calls. With CSP `connect-src`, I don't think that would be possible unless the new fetch targets are themselves on allow-listed domains (which is a totally separate issue). For example, consider a CSP of: `Content-Security-Policy: connect-src 'self' https://api.example.com https://api.example.com;`: This policy would allow fetch() requests only to the same origin ('self') and to https://api.example.com https://api.example.com, blocking any attempts to connect to other domains (typically with a corresponding warning/error in the browser dev console). That said, in fairness, CSP is of course only applicable to frontend code (not to backend JS, where anecdotally I've seen a lot more usage of `chalk` and some of the other pwned packags), but frontend code and the `window` object is what the OP used in their examples and seems like they're targeting w/ webpack, hence my mentioning CSP.
- riazrizvi 1y agoGlad to see this article raising awareness. Without fairness in the marketplace, the talent loses the will to play and the economy will further deteriorate. We are all suffering from an international trust breakdown from Covid, and now also from AI spam. If we don’t turn this tide, jobs and business opportunities are going to keep shrinking.
- deleted 1y ago[deleted]
- 4ndrewl 1y agoIf you're not vendoring, there's an argument to say that some portion of your source code is fair game to anyone who has commit rights to a variety of repos.
- withinrafael 1y agoIn July, packages were loading malicious DLLs (on Windows targets) [1]. It doesn't appear Lavamoat would help in that scenario. Is that right? If so, how do you mitigate this? Run everything in a container? [1] https://www.crowdstrike.com/en-us/blog/crowdstrike-falcon-prevents-npm-package-supply-chain-attacks/ https://www.crowdstrike.com/en-us/blog/crowdstrike-falcon-pr...
- mike-cardwell 1y agohttps://gitlab.com/grepular/safernode https://gitlab.com/grepular/safernode
- withinrafael 1y agoThanks will check it out!
- naugtur 1y ago1. Control lifecycle scripts with @lavamoat/allow-scripts 2. Do local dev with https://github.com/lavamoat/kipuka https://github.com/lavamoat/kipuka installed (I'm working on it) 3. If you don't permit the APIs used for loading DLLs they won't load themselves, so runtime protections are valid too. But I recall the DLLs were loaded in lifecycle script.
- withinrafael 1y agoThanks will check both out!