333 comments

[ 2.0 ms ] story [ 259 ms ] thread
PyPi should warn when the package and developer are new.
Yeah, an onboarding process build on trust and time-delay would be nice.
Yeah a time/activity based trust system like thepiratebay uses could be helpful.

Also devs should get into the habit of providing sha256 hashes on offical channels (i.e., github readme) so users can validate (if its possible to validate a pkg before executing malicious code in the python ecosystem, I'm not sure how that'd work).

> Also devs should get into the habit of providing sha256 hashes on offical channels (i.e., github readme) so users can validate (if its possible to validate a pkg before executing malicious code in the python ecosystem, I'm not sure how that'd work).

I would think the easy solution is to publish a public signing key per-person or per-project, and then sign individual files with that. So, GPG.

> Yeah a time/activity based trust system like thepiratebay uses could be helpful.

This is an excellent idea. Authors are something we are digging into heavily as part of an ongoing effort to improve trust in the open source ecosystem.

Doesn't pip's hash checking mode solve this issue? Freeze your requirements with hashes. Pypi already provides hashes for sdists and wheels. See https://pip.pypa.io/en/stable/topics/secure-installs/#hash-c...

If we are talking typos or other human errors, guess we could only warn people that there are other package with similar name available. Can't predict what people have in mind when they make a typo.

Even Firefox and Chrome's extension "stores" don't get this right. In either, a once trusted extension can be sold to a malicious company who then pushes new updates which automatically get downloaded by Firefox and Chrome by default, with no warning. Quite possibly without Mozilla and Google having any way of knowing it happened at all.

One way to address this is to move to a traditional "debian" style system, where packages are people affiliated with / known by Debian/Mozilla/Google, and specifically aren't the developers of the software themselves. The software is written by Developer X, but is then packaged and distributed by Packager Y, who ideally has no commercial affiliation with Developer X. If Developer X sells out to Malware Corp Z, end users can hope that Packager Y isn't part of that deal and prevents the malware from being packaged and distributed. This still isn't bullet-proof, but it's a lot better.

Totally agree. There should be a 30 day pe
I started to develop only inside VMs, with a full Desktop, IDE, browser etc. inside the virtual machine.

There have been to many contaminations of major package repos lately. Only one typo in an import statement up the dependency chain and you’d be compromised.

Virtual is part of a solution but not the key: the key is to separate your dev env from your real life/business environment -- including all your personal and professional business data and web accounts that expose your financials and private data.

If you log into your email from the virtual machine, you are at risk.

I think that separation is the point of the VM. Do the dev work in the VM, don't give it sensitive info about yourself.
That protects me (the software developer/maintainer) to some degree, but does nothing to protect the users of the software I am maintaining.
As long as your are creating web applications then browsers are pretty good at limiting blast radius of a single attacked website. Well, at least until attacker discovers that he can inject some fancy phishing into trusted site.

With local development environment it is a bit different, because unless you are running build/test etc. in a container/vm/sandbox, then attacker has access to all of your files, especially web browser data.

Development should be more exploratory and experimental than prod. For the past decade I've had a similar strategy: I freely install and demo new dependencies on separate dev hardware (or a VM when I'm on the road). Then I code review (incl. locked dependencies) and deploy from a trusted environment with reduced supply chain exposure.
The only place I log into from the VM is Github, protected by 2FA in case any malware gets my password.
The malware will just take the session cookie. Some actions still require 2FA approval, but it’s not many, iirc.
So the malware can delete all your projects or inject malware into them, but thankfully it won't be able to log in again later?
I've tried the same but the graphics performance was too slow (no GPU acceleration). The current setup is to use a virtual machine but connect to it via VS Code's Remote SSH extension from the host.
I hope you've turned off VS Code's "workspace trust" settings.

https://code.visualstudio.com/docs/editor/workspace-trust

Sometimes but I wonder to what degree it actually matters. Tasks, debuggers, extensions etc. run in the context of the VM, not the host. The Remote SSH extension turns VS Code into a "thin" client which presents pretty much just the UI.

https://code.visualstudio.com/docs/remote/ssh

Readme says: https://marketplace.visualstudio.com/items?itemName=ms-vscod...

> A compromised remote could use the VS Code Remote connection to execute code on your local machine.

So I would say that it might be a bit harder for an attacker to gain access to your local machine, but you should not rely on it, because it's more like security by obscurity.

Well damn. I was under the impression that the communication channel uses/accepts only well defined VSCode specific messages related to the UI...
Darn. Maybe the solution is to use vs-code client in the browser? Like vscode.dev or https://github.com/coder/code-server ? It limits what keyboard shortcuts and extensions are available, but at least it's in a secure sandbox on the client side.
Full disclosure, I am a co-founder at Phylum.

We are actively working on a solution that will fully sandbox package installations for npm, yarn, poetry and others.

It's rolled up as part of our core CLI [1], but is totally open source [2]:

[1] https://github.com/phylum-dev/cli [2] https://github.com/phylum-dev/birdcage

Sounds awesome.

Though I’m not sure of the solution really is / should be increased sandboxing.

The alternative may be a rethinking of the increasingly smaller packages. Maybe it’s better to have few large packages maintained by reputable organisations or personalities?

The problem is large, sprawling and complex. In an ideal case, we'd have high quality packages maintained by reputable people/organizations. But today this just isn't true. Open source takes contributions from a large number of unknown authors/contributors with motivations that may or might night align with your own.

We really need a defense in depth approach here. Sandbox where it makes sense, perform analysis of code being published, consider author reputation, etc.

Why is there so much discussion about sandboxing? Why wouldn't I put some malicious code in the package itself limiting myself to installation only?
A lot of the malware targeting developers is leveraging the installation hooks as the execution mechanism. So sandboxing the install helps stop this particular attack vector - which is why it gets talked about so much.

If you put code in the package itself, this would side step the "installation" sandbox. However we're also doing analysis of all packages introduced to the ecosystem to uncover things that are hiding in the packages themselves.

So you're right, we need a defense in depth approach here.

I think this is a very good idea. It should actually be built into the OS. I know of BSD jails, but not sure what else there is for Linux/Windows/MacOS.
> Only one typo in an import statement up the dependency chain and you’d be compromised.

Doesn’t even have to be a typo if the actual project is compromised. Like one of the 100s of NPM modules without 2FA for publishing.

This is a good approach, though presumably the VM still has access to your Github credentials (via the browser) and your SSH keys? It'll limit the fallout of getting owned to anything reachable from Github (is it against Github's TOS to have multiple accounts?), less if you have 2FA (does there exist 2FA for SSH keys (I don't mean passphrases)?), but I think it would be better for just my build/run/test cycles to be cordoned off into their own universe, with a way for just the source code itself to cross the boundary.
It might be too cumbersome for most, and I might be more paranoid than average, but each project for me means a fresh VM, a new Keepass database and dedicated accounts. Then again I work mostly in ops, and I've seen first hand how badly things can go wrong so isolation and limiting blast radius takes precedence over daily convenience for me.
Could you please share some resources/tactics for protecting your host machine from these development VMs? If I were to do this, I would want some assurances (never 100%) that my host is protected from the VM to the best of my ability.

(If it makes any difference, I would probably be using VMWare Workstation Pro)

I can't give you what you're looking for. You need to decide on the trade offs for yourself. There will always be a risk. Directed attacks can get out of VMs. You could slip up and log into a personal account inside the VM.
I tried to make it clear in my reply that I understood there are no guarantees. What I’m asking is if you have any guidance on reducing the likelihood of these attacks succeeding
That does sound incredibly cumbersome. I suppose that means you are an ace at provisioning machines.

How do you move data in/out of the guests? I always found that part of interacting with VMs to be annoyingly painful.

There are always trade offs. You do get better at things you do a lot. My mother won't use a password vault because copying and pasting is too much work for her. I'd just rather pay with my time and inconvenience than one day find out some python package I fiddled with for a late night project once means I need to call my bank.
SSH.

Doesn't even need to be command line, you can just open remote addresses in your favourite graphical file browser, at least under Linux.

Github offers fine grained personal access tokens. https://docs.github.com/en/authentication/keeping-your-accou...

Azure DevOps does it too

Far as I know, in AzDO you can't even limit a PAT to a single project/repository. Not good for limiting access cause even a read only can see private stuff in other projects. You might create a specific account and assign to only that project but what a pain.
Packj sandbox [1] offers "safe installation" of PyPI/NPM/Rubygems packages.

1. https://github.com/ossillate-inc/packj/blob/main/packj/sandb...

It DOES NOT require a VM/Container; uses strace. It shows you a preview of file system changes that installation will make and can also block arbitrary network communication during installation (uses an allow-list).

Disclaimer: I've been building Packj for over a year now.

Only secures installation, not runtime, but still helpful. I'm not a package maintainer, but I do wish that packages were not allowed to run any code at install-time.
This is goid defense in depth measure but doesn't solve one fundamental issue. You might be protected during developement by the sandbox but your users are not necessarily. I think we as developers should not give any sotware we do not trust to our users.
I'm following the same workflow. I use a Linux host and then a Linux guest with OpenGL acceleration on virt-manager. I do all my development and browsing inside the VM. I do not trust any of the npm packages or PIP packages. Any personal stuff like banking, password manager, Nextcloud goes on the host.

With modern virtio interfaces for network, disk and graphics practically giving near metal performance, there's no reason to not utilize VMs for development.

Similar. I run text editor on the main OS but run language server in the requisite environment with all the requirements in a container.
Open/free software is great when a great person writes some code and lets you use it, because they are kind and there's nearly no marginal cost.

But malicious actors can get value from polluting the sharing network, and that costs effort to defend against, which means someone(s) has to pay to secure the network, or be open to attack.

That’s not an either or thing. Someone you pay to can also be a malicious actor.
Or be compromised themselves and an unknowing vehicle for attacks (e.g. see SolarWinds).
Yes and it is common too. E.g. the vendor of my SmartTV now has a probe in my home.
It's using base64 encoded strings to deliver the initial stage. Can this be avoided/flagged more easily if by adding a scan of statements featuring base64 or import?
That'd catch a ton of valid packages. Right now on my random collection of packages in site-packages I have ~60 packages that have 'import base64' in them.
Yeah, it would probably create more manual work if you have too many false positives. I have maybe six base64 strings in the code I'm working on, so it might be worthwhile looking into provided my legitimate imports don't have any.
Yes, this works really well. But as soon as you deploy it, the actors change tactics. We've had to build a defense in depth approach to discovering malicious packages as they are introduced into the system.
it is probably very easy to bypass, by creating a sub package that can do the decoding via proxy functions, which is not evil at all, and depending on that package on the evil one. It won’t trigger the alarm, as it is indirectly depending on the base64 :)
Exactly, I don't think you can rely on a naïve import to determine maliciousness. We've basically had to build out heuristics that are capable of walking function calls for this exact reason. Otherwise things are just too noisy.
The way I'd go about this is probably starting a VM, installing the package and seeing what in the filesystem is affected by it rather than trying to do static analysis (which becomes a cat and mouse game as detection heuristics improve so do the stealth heuristics).

The attack surface area is too big when random python code is executed, which is the case for `setup.py`, but even if there wasn't code executed there, as soon as you import the package and use it, you'd have the same issue.

Unless you can hide the fact that it's running in a VM, I don't see why code couldn't act normally if it thought it was being analysed like this. Or what about some kind of payload that executes after a long delay, and would become visible for long running programs but not short tests? and so on.
This is exactly what Packj [1] scans packages for (30+ such risky attributes). Many packages will use base64 for benign reasons, this is why no fully-automated tool could be 100% accurate.

Manual auditing is impractical, but Packj can quickly point out if a package accesses sensitive files (e.g., SSH keys), spawns shell, exfiltrates data, is abandoned, lacks 2FA, etc. Alerts could be commented out if don't apply.

1. https://github.com/ossillate-inc/packj

Disclaimer: I developed this.

Is there something about Python or PyPI that makes it more attractive for malicious developers to add malware?

Is this also happening for repos for other languages (e.g. CPAN, RubyGems)?

I think this has more to due with the contexts/industries we typically see Python used in over pure popularity. If popularity was the only factor I'd expect to seeing a lot more news about these problems in Java/PHP ecosystems which are absolutely massive.
I mean, PHP is pretty much a domain specific language, and we're only about a year out from log4j.
Java is special since it has mandatory version pinning of all dependencies, doesn't run code at install time and uses a full url for dependency names. That means dependencies don't auto-update, don't compromise CI/CD as easily and don't get misspelled as easily (ie: people copy paste the whole name+version versus writing it from memory).

Many languages since then decided that causes too much overhead.

For what it's worth, this happens in pretty much all the ecosystems. We have seen similar behavior in NPM, rubygems, and others. PyPI is just really popular.
Yes, it's very very popular. And getting more popular every year.
It’s the most popular

It can run code during installation phase

It’s very easy to obfuscate due to its dynamic nature. An import of urllib or os.system isn’t immediately visible at the top of a file as it would be in Java, it can be hidden in eval() or basically anywhere.

Finally, even legitimate packages have very, and I mean it, very bad names. Usually with a trailing number for no good reason. Lacking organizational namespaces. Together with a culture of using a lot of such dependencies and lacking a culture of freezing transitive versions. Blending in among those is too easy. Just take a common name and suffix it with 3.

The article doesn't explain what exactly the "W4SP Stealer" does. Would someone be able to explain?
It downloads a script that, at least right now, will turn around and grab cookies and passwords from browsers and send the data off to an discord webhook.
> discord webhook

Hah. Is this true? I find it funny since IRC has/had this reputation for being a means of communication with malware and it's often blocked on this grounds.

Nice to know that malware is going on with the times and is using Discord for that now.

Discord is great as command and control server because the malware author doesn't need to expose their ip address or implement a complex web of proxy to secure their C&C server.
Couldn't you use someone else's IRC server, the same way you use Discord's server?
I suppose you could, but have you seen how popular new opensource projets being run these days? Young devs really loves discord to the point of hosting documentations there. I imagine young malware authors are no different.
Which, I don’t know if I’m getting old, but man that frustrates me. It’s a terrible platform for documentation. It’s barely a good text chat platform.
It's a slew of checks for passwords and other things on the developers machine. The data is extracted and sent to a remote endpoint controlled by the attacker.
The guy who runs the C2 openly has the source code for the stealer on his GitHub. Why doesn't GitHub do anything about this shit?

I've personally been hacked by a supply chain attack via a GitHub wiki link. I contacted GitHub support and didn't hear back from them for 3 months. They are completely useless.

They are trying. The level of effort to release these things is so low, the effort required to catch it and remove it at scale is much harder, unfortunately.
Are they? I know I'm biased because this affected me and I'm still mad about it, but I just don't buy it.

I contacted them, showing the plainly obvious malicious account that was distributing malware. Two months later, they send me a generic message saying that they've "taken appropriate action", but the account and their payload was STILL THERE, they hadn't done anything. The attacker was rapidly changing their username, and honestly I'm not sure their support staff has a way of even dealing with that. I tried to explain the situation as best I could, but they were not helpful in the slightest.

I don't know what their standard for 'malicious' is, but they nuked Popcorn Time and Butter (the technological core without the actual piratey bits) from orbit until there was a huge amount of backlash.
I'm not even asking them to deal with the problem "systemically" or "at scale". I just want them to respond when I am trying to stop an active criminal campaign whose goal is to steal money and cryptocurrency from people.
Talk to the FBI or any authorities, then.

I despise the idea of GitHub removing any code just because YOU (anyone) think they are criminals.

Read mr_mitm's comment. I have no problem with potentially malicious code being hosted on GitHub, I think it's a good thing. Using GitHub's infrastructure for your theft campaign is clearly not okay.
We're not talking about some quirky money-strapped startup. We're talking about Microsoft.
Does GitHub actually prohibit programs that are up front about the fact they do something questionable? Considering there have been active repos for those steam pirating DLLs on the site for ages I thought they only really go after hidden maliciousness
Considering the entire open source pentesting community almost exclusively uses GitHub to host their projects: no. There is actual malware being hosted on GitHub, with the caveat that malware and pentest tools or proof of concept exploits are sometimes indistinguishable.

GitHub announced a few years ago that they would crack down on malware and were about to introduce some very strict T&C. After a huge backlash from the pentesters (justified in my opinion), they backpedaled a little bit. Hosting pentesting tools is fine, using GitHub as your C2 server or to to deliver malware in actual attacks is not.

I completely agree, despite the wording of my comment. In this case, the user has a different GH account for hosting their malware and C2, but the fact that they're so flagrant about it is what bothers me.

I was a skid once, I get it, probably a lot of us were.

The standard HN answer is because freedom of speech. That the problem is the one using the code, not the code itself.
can there be a "blue checkmark" system for pypi authors? I'm sure that's been brought up and rejected for reasons.
Yes, this lines up with the "Critical Project" concept that has been floating around in the past year. It is... contentious to say the last. Previous HN discussion: https://news.ycombinator.com/item?id=32111738
This gives a checkmark based on number of downloads, so there is absolutely no guarantee that the package doesn't do anything malicious or won't in the future.
It's not going to be a "blue checkmark" per se, but we're currently working on integrating Sigstore signatures into PyPI. The idea there will be that you'll be able to verify that a package's distributions are signed with an identity that you trust (for example, your public email address, GitHub repository name, or GitHub handle).
I think the issue isn't so much malicious authors, it's compromised repositories and compromised repositories as dependencies.

Blue check would gatekeep a lot of noble, new developers.

these aren't compromised repositories on pypi, these are fully legitimate repos started by bad actors that intentionally use similar sounding names and readmes. a system by which all of the major packages and dependencies are coming in, which to be clear is the whole pile of stuff that people aren't usually looking at, on a set of trusted authors and where someone who is being careful can whitelist specific repos for newer projects they want to use would reduce the problem to almost nothing.
SQLAlchemy can get one for $20/mo : - D
we have more than enough donations to pay for that, and since SQLAlchemy is a "critical project" they'd likely comp us anyway.

but IMO they'd never charge for such a thing, that's not at all in the spirit of OSS / python dev.

(comment deleted)
This is the double-edged sword of open-source. It's awesome because anyone can contribute. It can be dangerous for the same reason, unfortunately.
I don't think it makes much sense to verify pypi authors. I mean you could verify corporations and universities and that would get you far, but most of the packages you use are maintained by random people who signed up with a random email address.

I think it makes more sense to verify individual releases. There are tools in that space like crev [1], vouch [2], and cargo-vet [3] that facilitate this, allowing you to trust your colleagues or specific people rather than the package authors. This seems like a much more viable solution to scale trust.

[1]: https://github.com/crev-dev/crev [2]: https://github.com/vouch-dev/vouch [3]: https://github.com/mozilla/cargo-vet

We've found a lot of open-source packages that are authored by (well, released by authors identified by) disposable email addresses. We were shocked to find companies doing this, too.

Package Dependency land is a crazy place

The reason is obvious, people crawl pypi.org/github.com/npmjs.com and email their job posts or product launches. Every platform that requires an email and shows it publicly will necessarily get a lot of disposable ones.
Identity verification will never be enough, if their account or anything in their development or distribution pipeline is compromised, so will their code. Sandboxing mechanisms are fundamentally required - not only to ward off malicious attacks there, but to prevent accidental side effects and compromise at runtime too.
yes I knew someone would say, "that wont solve the problem!" it would certainly make it way better. it is much more difficult to hack someone's account and bypass their two-factor authentication instead of literally uploading any number of randomly named packages at will.

the same argument can be applied to, "when you park on the street in manhattan, close your windows and lock your doors". Well that won't save your car from being compromised. But it will certainly make it way less likely that someone will steal something out of your car.

These sorts of things is why D doesn't allow any system calls when running code at compile time, and such code also needs to be pure.

Of course, this doesn't protect against compiling malicious code, and then running the code. But at least I try to shut off all attempts at simply compiling the code being a vector.

I'm honestly not sure the benefits of executing code during compilation/install outweigh the bad. Most attacks we have seen leverage this as the attack vector.
The problem comes when you need to do something bespoke and custom, like building a C dependency so you can link it into your Python (or whatever language) library. Sometimes your options are "run a makefile" or "reimplement an entire library from scratch". I'm not saying that this isn't a problem; it is. I think the better solution is transparent sandboxing for dev environments.
> I think the better solution is transparent sandboxing for dev environments.

I don't disagree at all. We're building an open source sandbox for devs right now for this exact reason. Linked it in another comment.

I'd love transparent sandboxing -- but the difference between me wanting to install the awscli and something that steal awscli's credentials is only a matter of intent, so a bit difficult.

I've basically converted to doing all node development in docker containers with volume mounts for source, now it looks like python is going to need to be there as well, at least for stuff that pulls in any remote dependencies.

CTFE (Compile Time Function Execution) is a major feature of D, and has proven to be immensely useful and liked.

Note that CTFE runs as an interpreter, not native. Although there are calls to JIT it, an interpreter makes it pretty difficult to corrupt.

I've never understood this position. How often do you add a dependency to your project, compile your project, and then never run your project ever? I can't think of a single case where this would have protected me.
CI/CD servers, dev laptops etc could have more privileges than the production machines. For instance.
So you never run tests on your dev machine or CI/CD, and never run your code to manually test?

I'm not experienced but I thought it was normal to have some way to try out what you've written on your dev machine. Is everyone else stepping through code in their head only, and their code is run for the first time when it's deployed to production?

Your dev machine getting pwned is bad, but your CI server getting screwed up is worse.

This way you don't need to sandbox the compiler, and it can freely use system resources and access source trees. You only need to sandbox the execution.

(As some people point out in this thread, editors are starting to use compilers to get overall meta-information, too-- if you can't even -view the code- to tell if it's malicious without getting exploited, that's bad).

> This way you don't need to sandbox the compiler, and it can freely use system resources and access source trees. You only need to sandbox the execution.

If this is now only helping CI and not dev machines I don't see why it's worth the effort. Wouldn't it be much simpler and more reliable to just sandbox compilation of anything in your CI?

> if you can't even -view the code- to tell if it's malicious without getting exploited, that's bad

I guess? I can't think of a single time in my life where this would have practically helped me.

I skim dependencies on GitHub for obvious red flags and then trust them. I assume places with the resources to do actual in-depth review can disable advanced analysis in their IDEs for that.

You're digging in really hard trying to talk someone out of following good development practices because you don't personally think his effort is worth it. Personally, I don't think the effort being put into this argument is worth it.
> Wouldn't it be much simpler and more reliable to just sandbox compilation of anything in your CI?

Not really. The compiler has to be able to access large swaths of your code. You want to e.g. keep your code safe. You would have to have very finely-grained sandboxing to prevent substantive disclosure, and even then you're likely leaking information.

Fancy IDEs perform code analysis and thus if you feed them something malicious I guess it’s feasible to run a shell command or similar. By definition, IDEs have to do that to compile code, run linters etc.
It means you don't need to run the compiler in a sandbox. People do not expect the compiler to be susceptible to malware attacks, and I do what I can to live up to that trust.

I haven't heard of anyone creating a malicious source file that would take advantage of a compiler bug to insert malware, but there have been a lot of such attacks on other unsuspecting programs, like those zip bomb files.

> People do not expect the compiler to be susceptible to malware attacks

I'm not familiar with D, so I'll use the example of Rust. My usual workflow looks something like this

1. Make some changes

2. Either use `cargo test` to run my tests or `cargo run` to run my binary.

In both those cases the code is first compiled and subsequently run. I care if running that command gives me malware. I don't care at what step it happens.

Perhaps it’s about responsibility. It’s not the compilers fault if you chose to compile and run malware. But you could blame the compiler if it ran malware during the compilation process.
All else equal I'd agree. But I'm perplexed why people spend a lot of effort on what seems to me like a purely philosophical benefit.
It's just address part of the problem, which of course is why it seems somewhat pointless. I need to:

1. Install packages/deps/libraries etc safely

2. Run code that includes those libraries that limits their capabilities centrally.

> It's just address part of the problem, which of course is why it seems somewhat pointless

I cut my teeth in the aviation industry, where the idea is to address every part of the problem. No one part will fix everything. Every accident is a combination of multiple failures.

It's not philosophical. All people who write programs that consume untrusted data should be actively trying to prevent compromise by malware.
In general, I agree. I think developer tools are a special exception because there are so many gaping vulnerabilities inherent to it it's meaningless.

I think of that kind of thing as the equivalent of "your laptop won't be vulnerable on odd-numbered days". That'd be a great plan if there was a pathway to going from there to no vulnerability. If that was the low-hanging fruit and you're stopping there it's a complete waste of time.

With rust quite often (e.g. if you are running rust_analyzer) it will run `cargo check`, to produce errors. When `cargo check` is run, build.rs compiled and run. So quite often by step 1, just opening the file in your editor before even making any changes code is compiled and run.

Walter's solution here allows the compiler to be used by the editor without the editor being susceptible. Which at the very least negates the need for a pop-up in your editor asking for permission.

> With rust quite often ... it will run `cargo check`

Yup. But making "cargo check" safe while "cargo run" stays vulnerable just reduces the number of times you run malicious code. And whether malicious code runs on my laptop every time I edit a file or every hour or every week makes absolutely no difference. One run and the malware can persist and run whenever it wants going forwards.

> Which at the very least negates the need for a pop-up in your editor asking for permission.

My argument is that the pop-up is security theater. I've disabled it, I don't think it should be enabled by default.

[1]: I'm handwaving slightly to get from "your code depends on a malicious library" to "malicious code is run". If I recall correctly there's linker tricks that could do that, or you could just have every entrypoint call some innocuous sounding setup function that runs the malicious code.

only if you intend to run the program though, if you want to just read the source code, perhaps to see if it contains malicious code you really don't want your editor doing such things by default, so something has to give.
(comment deleted)
Some people try to inspect code outside of a project before including it into something.

Say there is a Github repo with a crate called "totally_safe_crate", and I want to use this crate in my project, but I am not sure whether I can trust it or not. What do I do?

What I would likely do is clone the repo, then open my editor and look through the source and whatnot and make my decision.

In this case I never intend to run "cargo run" at all, but I may want to run an LSP server to help inspect the code, or I may accidentally enable LSP in the editor out of habit or something.

In this case, it would be nice if I could be certain that simply inspecting and reading code was safe, but as it stands now, in Rust, this is not the case. We can't even inspect code to make sure it's safe unless we open the source files in a "dumb" editor.

In your example you are just adding a crate to cargo.toml and firing off cargo, so of course it's not going to be useful there. Some of us may want to be more cautious than that and actually read code before putting it in our projects.

In a previous HN discussion on the topic of rogue Python packages, readers had suggested bubblewrap and firejail for sandboxing. They limit the access a script and its packages have to your filesystem and network.

I think that's the better approach - just assume all packages are malicious by default. Can't rely on scanners because of the large number of packages and attacks.

It's a problem with every open ecosystem where libraries can be downloaded and run. Rust, Golang, Node all have the same problem. That's why I think it's better to assume anything we download is malicious. Stuff like Bubblewrap and Qubes OS seem to be the better approach compared to relying on vulnerability hunters and scanning tools.
Do both and more. When using an unfamiliar package check it’s upload history. How far does it go back? How did I discover the package, do i trust that source? Etc.

Unless your code is never going to touch important data or resources, like for example (but not limited to) being used commercially in any vein then you can’t keep it in a padded cell forever.

That's not going to help much if code from the malicious attacker is still going to end up integrated into the software product being built.
I disagree. I need to write this up in more detail but it helps quite a lot.

1. An attacker who can access your development environment and your production environment is worse than one who can only access your production environment. You might say "but the end goal is prod", but it's not that simple because of (2).

2. We already have very good tooling for isolating services at runtime. Separating them onto different instances, firewall/security groups, limited API keys, docker/ containers, apparmor, selinux, etc. We have a lot of tooling for "a service in production is owned". What we lack is "a library in dev environment is owned".

3. Devs often have more privileges than your services. It's unfortunate but at a lot of companies, perhaps given some lateral movement around dev envs, you'll find SSH keys to production, browser session cookies that give you console access, source code, chat sessions, internal documents, git keys, gpg keys, etc.

So I'm actually fine with a tool that sandboxes the build process but leaves open the hole of "but the attacker can patch the binary and execute code in production". That's a huge win.

Based on my experience, tools like firejail, ebpf, and opensnitch help us keep security in the forefront, train us to verify behavior instead of trusting blindly, and even persuade end users towards that mindset through our installation steps.

If we can spot odd behavior during development and eliminate it from our stacks, the product will be more secure for end users too.

There was a time when convenience overrode any security doubts in my mind. But now I routinely use these tools to restrict access, monitor, and review runtime behavior.

Do both and more. When using an unfamiliar package check it’s upload history. How far does it go back? How did I discover the package, do i trust that source? Etc.

Unless your code is never going to touch important data or resources, like for example (but not limited to) being used commercially in any vein then you can’t keep it in a padded cell forever.

The issue is where your tools are supposed to be generally available on a machine or when your application has access to secrets (like keystores, configuration files, log files, etc.) which is pretty much every application.
So that means you can never use any package in code that has to handle sensitive data or manipulate the host machine?
No, it means don't trust it blindly but instead learn techniques to monitor and verify what it does. I use firejail, ebpf, and opensnitch to restrict access, monitor, and verify runtime behavior.

Where possible, persuade end users too to be equally careful. The "linux is safe" cliche blinds both us and end users to its obvious security problems, like running every script as the logged-in user with the same level of access. These malware developers know it and rely on it. That's why we need to move everybody towards a restrict-monitor-verify mindset by default.

Plug: I've been building Packj [1] to address exactly this problem. It offers “audit” as well as “sandboxing” of PyPI/NPM/Rubygems packages and flags hidden malware or "risky” code behavior such as spawning of shell, use of SSH keys, and mismatch of GitHub code vs packaged code (provenance).

1. https://github.com/ossillate-inc/packj

I agree, "assume unknown, unaudited packages are malicious" is the ideal stance. However, I would say that a simple scanning approach could probably take you pretty far. For instance, if you're not using the requests module or the socket module, chances are pretty good there's no data exfiltration going on.

It's absolutely not a foolproof approach, but it is a lightweight layer that can be used in a "defense in depth" approach.

In Python, dynamic imports exist, making this impossible
I don't see how having dynamic imports matters if all you want to do is detect if a specific file is imported. Run the install and see what gets imported. That's it.
If you actually have to execute a program (but have no safe way of doing so), to see if a complex routine that may return any filename imports a safe file or not, then you are facing up against https://en.wikipedia.org/wiki/Rice%27s_theorem
So? Any method of detecting a "malicious package" faces Rice's theorem, unless you want to claim that "malicious" is a trivial property.
Which is why any approaches relying on identity verification or scanning are bound to fail - sandboxing/capability security MUST become built into languages
Are you suggesting that you would rather wait for languages to provide robust sandboxing capabilities and not use available static/dynamic analysis tools (e.g., Packj [1]) to audit packages for malicious/risky indicators, particularly when we hear about new attacks on open-source package managers almost every week?

1. https://github.com/ossillate-inc/packj [Disclaimer: I built it]

No, I'm merely suggesting that they are not a solution to the problem, and that the fundamental issue has to be approached at the language level.

What you are building is mainly a smoke detector (and maybe a bit of a sprinkler if it takes some decisions itself), not fireproof doors (that only at install, not test- or runtime). Smoke detectors by themselves cannot prevent fires from spreading and are not completely reliable.

Analysis tools are still useful - even with perfect language-level access and resource control, packages which are given many required permissions may behave maliciously (e.g. through compromise of any component in the development or distribution pipeline), or return malicious data (which is out of scope/unsolvable at the language level). Both approaches complement each other nicely.

Another good option is to create a new user and run everything under the new UID. Running under a new UID has less chances of accidentally leaving something exposed that can allow for sandbox escape.

If you run everything from the new UID, it will mostly be contained to it's own $HOME directory and be unable to modify your user's files or system files. Some distros do not protect home directories from being read so it might be worth setting your actual user's $HOME to umask 0700 or whatever.

If you are using bwrap while running X11 and not running the sandbox with a new UID, the sandboxed processes may be able to escape via the X11 socket! This can happen even when you don't mount the X11 socket into the sandbox (see abstract sockets)! I think unsharing the network namespace fixes this specific issue (not 100% sure), but there are probably more subtle footguns like this.

I really suggest running Wayland with XWayland disabled, and the Wayland socket protected from the sandbox if you want to use bwrap for security purposes!

How do you escape the sandbox through a Wayland or X11 socket? Do you have specific code examples?

Is there no way to safely run graphical applications in a bwrap sandbox? I thought Wayland was supposed to be better about this.

I think Wayland is fairly safe, but any X11 client can take screenshots or listen to the keyboard, or emit keyboard event, without limitations.
I do not have a specific code example, but you can use the normal X11 client interfaces to interact with the X server, which allows a lot of dangerous things such as sending events to other clients. We can imagine a rouge X11 client spawning a terminal and entering text through a virtual input interface, to run an arbitrary command for example.

On Wayland, assuming you don't have XWayland enabled and running, it depends on the specific compositor you are using and what Wayland protocols it supports.

Sandboxing GUI stuff on Wayland requires at the very least not having XWayland running, and also requires understanding what the compositor allows clients to do by default. Some compositors may have permission dialogues that prevent clients from doing stuff that you didn't expect.

Another options is "nsjail". I landed on it having considered at firejail, apparmour, selinux, bubblewrap.
What are the advantages?

I'm very frustrated with firejail since I can't for example block execution in my home directory, with the exception of one subdirectory.

It just can't be done.

Ive contracted on a project where another, IMO better, solution was employed. This was ruby.

The only allowed package (gem) server was one ran by the project. This package-server scanned, vetted and manually checked any version of a lib before "publishing it".

If you wanted to e.g. upgrade a package, you'd have to do this on this server first. It would then go through some steps, -automatic scanning, risk analysis, sometimes even needing the eyes of someone from a security team. After that the package was published on this server, and you could pull it onto your dev machine and use it in CI/staging/test/prod etc. Similar steps, to get a new package listed.

IMO this is better, because it stops supply-chain attacks before they hit your code, not after they've (potentially) infected the system.

Edit: for clarity "only allowed package" wasn't enforced very strictly. A linter and CI would catch any changes to code that would want to fetch packages from elsewhere. It wasn't to protect against rogue developers, but against "stupid me, accidentally upgrading to a version that is infected" and such.

This is one reason I prefer Debian python packages.
There's also a disturbing new trend of publishing end-user software as pip packages instead of apt-get packages, just because the bar to join apt-get is too high.
This really is the sweet spot when production is a specific Debian version, set up your dev environment to match that and it's pretty bombproof. Run CI with pip installs against later Python versions to see the shouty deprecations you'll be able to sidestep.
This is definitely a benefit of using distro-provided packages.

You get some vetting, and in addition, standard practice for many distros is to build everything that goes into the repos in sandboxes or VMs which have restricted or no network access. Additionally, some package managers incorporate that kind of sandboxing into their builds categorically, like Nix and Guix. (For Nix, this may only be on Linux— there are issues with sandboxing on macOS.) So if you build your project's dependencies via Nix or Guix, you're also protected.

This only protects you from `setup.py`-type (build time) attacks, of course. If the distro packages get compromised in some other way so that malicious code ends up in your installed programs (this attack has elements of that, IIRC), you're still in trouble.

I hope the age of a thousand dependencies automatically pulled and upgraded on a basis of trust is coming to a close. It was obvious from the start this would eventually become a problem. Trust-based systems like this only work for as long as scoundrels remain out of the loop.
(comment deleted)
A lot of people in this thread are asking for a reputation/"verified user" solution for this, but really I think just pulling a gazillion dependencies for applications is just all around bad. I actually think having a reputation system would be even worse, because people would see it and assume that reputation is a guarantee of safety. Trust without verification is where issues can become even worse.
Based on my experience with shady plug-ins for e.g. Photoshop back in the late 90s/early 00s, all that a reputation/"verified user" solution is going to achieve is a very lucrative black market of high-reputation/verified user profiles and credentials.
(comment deleted)
So is .NET finally going to make a comeback? Yes, you need some dependencies for projects, but in general Microsoft does a good job providing a lot of tooling and libraries.
I am really surprised that there haven't been even more malicious packages distributed in the past couple of years considering the rise of cryptocurrency. Seems like a determined and malicious actor could score big by targeting the more popular wallets.
It's totally happening. We've seen packages targeting a lot of the big exchanges. Most of the packages are targeting developers directly though; attempting to exfil the users wallets/keys.
This is a fundamental flaw in most langs. There should be a smarter way to track changes in what is specifically used in dependencies.
Once a buddy and I reverse engineered some JS on a site that did the same thing - sent you down one rabbit hole, more obfuscated code, etc.. etc.. we eventually got to the end of it and discovered a comment:

// help my name is ###

// i am being held at #### (address in china)

// please contact my family ###

(this was in chinese, we had to translate it)

Scary!

So did it seem like some kind of weird scam, or what?
Presumably it's to trick whitehats into tipping off the hackers that their code was being analysed and had been successfully deobfuscated, so the hackers knew they needed to move to a different attack.

It's actually quite devious, like a reverse honeypot that the bad guys use against the good guys, exploiting their empathy.

No, maybe a joke? Or serious...
From a web page so crammed with JavaScript that it's pointless to even try to take a look at the article.
> Upon first glance, nothing seems out of the ordinary here. However, if you widen up your code editor window (or just turn on word wrapping) you’ll see the __import__ way off in right field. For those counting at home, it was offset by 318 spaces

Haha, simple & effective...

So malicious packages for JS, now python, but still no W4SP stealers for libc :(

Feeling a bit left out, guys! How will my code get compromised randomly?

I wonder why we can’t have pip packages be published by username or organization, like

    pip install google/tensorflow
It would significantly reduce the attack space
npm does something similar with their scoped packages. It fixes the problem for the top level packages, but you'd still have to contend with the transitive dependencies written by smaller organizations or individual contributors. In this case, you have to guarantee that no one involved in the dependency chain ever typos anything.
This is true, and wouldn’t remove the entire space of attack, but would still limit it to some extent.
Oh absolutely. Unless everyone wants to be cool and stop publishing malware, gotta take a defense in depth approach here.
"you'd still have to contend with the transitive dependencies written by smaller organizations or individual contributors" - generally these are the higher risk dependencies anyway and should probably be used with extra caution anyway.
It gives false sense of security. What about google_official/tensorflow
It would still be an improvement if companies make clear what their namespace is.
Perhaps something like Docker hub where "official" images are like "/_/nginx"

So "_google/tensorflow" would be official.

"google/tensorflow" would not be (plus it would be reserved by default to avoid confusion).

What if the package is signed by a key available at google.com/pypi/key ?

Actually it should be not just a key but a whole TLS certificate, with references to a CA, activity dates, etc.

How would you know where you’d find that key?
Again Maven has the answer: if your namespace is a domain name you own, then the key needs to be available at a well-defined path on that domain (or as a TXT record in it).
Maven had this 20 years ago

quite why python refuses to learn from anything that went before it I really dont know

“Namespaces are one honking great idea — let's do more of those!”

one of main issues I have with java is how messy it is to import external modules. python is a breath of fresh air comparitively. introducing this kind of thing as mandatory is a step away from that
It makes me very sad that something as wonderful as code, the closest we have to actual magic is tainted by this. You know how to code and you chose to spend your time doing this? What a shame
Lots of “reputable” devs write code that’s every bit as shitty as this. Somehow it’s ok when all you’re doing is spying on your users and shoving ads into their eyeballs.
One of the fake packages is called `felpesviadinho`, which looks like calling someone named "felpes" with a homophobic slur in Brazilian Portuguese.
This type of stuff is one reason I like vendoring all my deps in golang. You have to be very explicit about updating dependencies, which can be a big hassle, but you're required to do a git commit of all the changes, which gives you a good time to actually browse through the diffs. If you update dependencies incrementally, it's not even that big a job. Of course, this doesn't guarantee I won't miss any malicious code, but they'd have to go to much greater lengths to hide it since I'm actually browsing through all the code. I'm not sure the amount of code you'd have to read in python would be feasible, though. Definitely not for most nodejs projects, for example.

I think it's an interesting cultural phenomenon that different language communities have different levels of dependency fan-out in typical projects. There's no technical reason golang folks couldn't end up in this same situation, but for whatever reason they don't as much. And why is nodejs so much more dependency-happy than python? The languages themselves didn't cause that.

> And why is nodejs so much more dependency-happy than python?

Could it be that nodejs has implemented package management more consistently and conveniently than other languages/platforms?

That's one thing, the other is the almost complete absence of a standard library.
Yeah, I think this is a big one. One of the things that I have always liked about Golang is that the standard library is quite complete and the implementations of things are (usually) not bare-bones implementations that you need to immediately replace with something "prod-ready" when you build a real project. There are exceptions, of course, but I think it's very telling that most of my teammates go so long without introducing new dependencies that they usually have to ask me how to do it. (I never said the ux was fantastic :) This also goes to GP's "consistent and convenient" argument.
Totally agree. It feels like there is a pretty strong inverse correlation between standard library size, and average depth of a dependency tree for projects in a given language. In our world, that is pretty close to attack surface.
Rust is another example of this. Just bringing in grpc and protobuf gets about a hundred dependencies. Some of them seemingly unrelated. For a language aimed at avoiding security bugs, I find this to be an issue. But a good dependency manager and a small (or optionally absent) stdlib has lead to highly granular dependencies and bringing in giant libs for tiny bits.
pip throws your dependencies in some lib directory either on your system (default if you use sudo), in your home directory (default if you don't use sudo), or inside your virtualenv's lib directory.

npm pulls dependencies into node_modules as a subdirectory of your own project as default.

Python really should consider doing something similar. Dependencies shouldn't live outside your project folder. We are no longer in an era of hard drive space scarcity.

As of Python 3, pip install into the system Python lib directory is strongly discouraged. ISTR that even using pip to update pip results in a warning.

That’s not to say that there’s not still some libs out there that haven’t updated docs to get with the times.

More distros should adopt the Debian practice of installing into dist-packages and leaving site-packages as a /usr/local equivalent for pip to use on it's own.
Have you seen how much space a virtualenv uses? It can easily be >1 GB. For every project, this adds up. (Not to mention the bandwidth, which is not always plentiful).
4TB hard drives are $300 these days.
4tb HDDs are closer to 80$ now, but that reinforces your point :). Even SSDs are now close to 300$ for 4tb!
Yeah i meant 4TB SSDs, who uses magnetic HDDs anymore lol
Well, npm uses a cache so it won't re-download every package every time you install it.
The problem is the tree of dependencies you might check. Sure you can check the changes in a direct dependency, but when that dependency updates a few others and those update a few others, the number of lines you need to read grow very quickly
Golang flattens the entire dependency tree into your vendor directory. It's still not that big. The current project I am working on has 3 direct external dependencies, which expands out into 22 total dependencies, 9 of which are golang.org/x packages (high level of scrutiny/trust). It's really quite manageable.
Indeed, gophers often make it a point of pride to have no dependencies in their packages.
> And why is nodejs so much more dependency-happy than python?

Part of it—but I'm sure not all—is that the core language was really, really bad for decades. Between people importing (competing! So you could end up with several in the same project, via other imports! And then multiples of the same package at different versions!) packages to try to make the language tolerable and polyfills to try to make targeting the browser non-crazy-making, package counts were bound to bloat just from these factors.

Relatedly, there wasn't much of a stdlib. You couldn't have as pleasant a time using only 1st-party libraries as you can with something like Go. Even really fundamental stuff like dealing with time for very simple use cases is basically hell without a 3rd party library.

Javascript has also been, for whatever reason, a magnet for people who want to turn it into some other language entirely, so they'll import libraries to do things Javascript can already do just fine, but with different syntax. Underscore, rambda, that kind of thing. So projects often end up with a bunch of those kinds of libraries as transitive dependencies, even if they don't use them directly.

It’s worth mentioning that Underscore started before browsers widely implemented the same features in standard JavaScript. Underscore is much less necessary now that Internet Explorer EoL’d.
It also blows up the size of your git checkouts pretty fast though.

I don't think you really gain much either; vendoring was useful before modules, but now we have modules and go.sum I don't really see the advantage. If you have "github.com/foo/bar" specified at version 1.0.4 the go.sum will ensure you have EXACTLY that version or it will issue an error in case of any tomfoolery.

Vendoring also means your builds don’t need an Internet connection.

Going on a trip somewhere without an Internet connection? Checkout the repo on your laptop and go. Without vendoring: oh shoot, I forgot to download the deps, I guess I’m going to be forced into a work-life balance. With vendoring: no additional step needed after checking out the repo. The repo has everything you need to work.

Another case: repo of your dependency is removed, or force-pushed to overwriting history. You’ve lost the ability to build your project, and need to either find another source for your dependency, or rewrite it. With vendoring: everything still works, you don’t even notice the dep repo went under.

Generally, with vendoring your code is in just one place instead of being a distributed being which crumbles when any part of it gets sick.

Moreover, relying on checksums to me seems a bit overcomplicated. It’s like going to a pub and giving each drink from a stranger to a chemist for verification to make sure they didn’t slip any pills, when you could just carry your own drink around and cover the top with your hand.

You should have the modules downloaded to the module cache for the occasional case when you don't have direct internet access.

> Another case: repo of your dependency is removed, or force-pushed to overwriting history. You’ve lost the ability to build your project, and need to either find another source for your dependency, or rewrite it.

The GOPROXY (https://proxy.golang.org/) still contains that removed repo, and since everything is summed people can't just force overwrite it. Plus, you still have it in the module cache locally.

You can of course always come up with "but what if...?" scenarios where any of the above fails, and all sort of things can happen, but they're also not especially likely to happen. So the question isn't "is it useful in some scenario?" but rather "is it worth the extra effort?"

> Moreover, relying on checksums to me seems a bit overcomplicated.

It's built-in, so no extra complications needed.

> You should have the modules downloaded to the module cache for the occasional case when you don't have direct internet access.

That’s assuming I’ve built the thing previously on that same computer. I’m talking about the common case of working on a normal desktop day-to-day and then switching to a laptop, when travelling to a place without internet (or internet of such a poor quality you might as well not bother). With vendoring I don’t need to think about any other steps than copy/checkout the repo. The repo is self-contained. Without it, I’m making the quantum leap to a checklist.

You need internet access to either checkout or update the repo; you can use "go mod download" (or just go build, test, etc.) to fetch the modules too. It's an extra step, but so is vendoring stuff all the time.

But like I said, it's not about "is it useful in some scenarios?" but "is it worth the extra effort?" I'm a big fan of having things be self-contained as possible but for this kind of thing modules "just work" without any effort. Very occasionally you might go "gosh, I wish I had vendored things!", but I think that's an acceptable trade-off.

That bit with the semicolon way off to the right side of screen is kind of sloppy. It's a dead giveaway of "I'm doing something I shouldn't be doing"