Live data from Hacker News

Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

medium.com

321–330 of 412 posts

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#321
post #117

Pulling packages down at build time seems ludicrous to me, I can understand it in a development environment, but I don't understand how "Pull packages from the public internet and put them into our production codebase" past any kind of robustness scrutiny. I guess it's a case of the ease of use proving too great, so convenient in fact that we just kind of swept the implications under the rug.

Migrating from public NPM to a privately-hosted, your own mirror of NPM is not a very complicated process, and if you already have a CI pipeline in place, it can be implemented completely transparently to developers. But as many other things that an organisation has to change as it grows from a single-founder startup to a real company, it's something many people just forget to do until they face the consequences.

The goal of verdaccio is to make this less complicated. https://github.com/verdaccio/verdaccio

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#322
post #305

I don't understand why there is this issue. We publish our internal npm packages in the @company namespace and we own this namespace on the public npm registry. Problem solved, isn't it?

Yes, I'm confused by this too. Scoped packages on npm solves this problem, yet it isn't mentioned in the article at all.

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#323
post #183

Earlier quoted context omitted.

Maven is/was huge in the Java world. For years it was pretty much the only way to resolve dependencies, until people got fed up with its many idiosyncrasies. > “we are getting .jar files individually and not using maven because it's a fucking mess” That seems odd and a bizarre edge case. Nobody worked like that with Java projects, and I bet nobody does today either.

I think you're confusing the client side build tool Maven with the artifact repository Maven. Gradle for example still uses Maven for its dependency artifact repository. So Maven is still the standard for Java.

I am confusing what now? I'm the one arguing Maven is huge and that the parent post I'm replying to is mistaken. I never mentioned Gradle, that was a sibling comment.

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#324

Earlier quoted context omitted.

Maven is ubiquitous in the Java world and the de-facto package/dependency management system out there. Has been since the mid-2000's and as of 2018 when I last did Java development (Scala really), it is still widely in use. Getting jar files manually would have me running from whatever company that was doing that. Let me guess, they wrote all their code in Notepad because IDE's are a "fucking mess" too right?

You vastly underestimate the level of bureaucracy that can exist in the biggest Java users of this planet (namely banks and public administrations): in these organization (at least a few years ago, the Solarwind attack shows it may not be the case anymore) every single dependency you want to use must be justified, and then is audited by a dedicated team, which ends up handing you the validated .jar. It was a common d…

So you worked with customers using some particularly strict vetting protocols. That's a far cry from claiming Maven never reached the popularity in the Java world that NPM has in the Javascript world -- Maven is the dependency manager in the Java world. The entities you worked for are the exception.

Another thing that strikes me as odd in your comparison: those customers you worked with wouldn't have used javascript+NPM either, since it has all of the problems of Maven and external deps and then some! So what exactly are we comparing then?

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#325

Earlier quoted context omitted.

I'm not familiar enough with Java to have a strong opinion on this, but this HN comment from the linked article mentions that you can only have one SecurityManager per app, so sounds like that's still too coarse-grained: https://news.ycombinator.com/item?id=18599365

Oracle’s own secure coding guidelines for Java [1] actually now recommend adopting a capability-based approach rather than relying on SecurityManager: > FUNDAMENTALS-5: Minimise the number of permission checks Java is primarily an object-capability language. SecurityManager checks should be considered a last resort. (Note: quite a lot of Java’s standard library is not designed along object-capability lines so you sho…

They are not in tension. The Java security architecture is a mix of capability and module-level security.

It's probably worth posting a quick refresher. The system is old but people don't use it much these days, and the documentation isn't that good. At one point I wrote a small JavaFX PDF viewer that sandboxed the PDF rendering code, to learn the system. I lost the source code apparently, but the hard part wasn't coding it (only a small bit of code was required), it was learning how to configure and use it. I tested the sandbox by opening a PDF that contained an exploit for an old, patched security bug and by using an old, vulnerable version of the PDFbox library. The sandbox successfully stopped the exploit.

Fortunately the Java team still maintain the sandbox and via new technology like the module system and GraalVM, are reinforcing it. In fact, GraalVM introduces a new sandboxing technology as well that's simpler to use than the SecurityManager, however, it's also probably less appropriate for the case of blocking supply chain attacks.

Java's internal security is based on two key ideas:

1. Code that can protect its private state. When the SecurityManager is enabled and a module is sandboxed, it isn't allowed to use reflection to override field or method visibility.

2. Stack walks.

Let's tackle these backwards. Imagine it's time to do something privileged, like open a file. The module containing the file API will be highly privileged as it must be able to access native code. It will have a method called read() or something like that. Inside that method the code will create a new permission object that represents the permission to open files under a certain path. Then it will use AccessController, like this:

   FilePermission perm = new FilePermission("/temp/testFile", "read");
   AccessController.checkPermission(perm);
The checkPermission call will then do a stack walk to identify the defining module of every method on the stack. Each module has its own set of granted permissions, the access controller will intersect them to determine what permissions the calling code should have. Note: intersection. That means if any unprivileged code is on the stack at all the access check fails and checkPermission will throw an exception. For example, if an unprivileged module registers a callback from a highly privileged module, that doesn't work: the low privileged module will be on the stack and so the privilege is dropped.

Access control contexts are themselves reified as objects, so instead of doing a permission check immediately you can 'snapshot' the permissions available at a certain point and use it later from somewhere else. And, starting a thread copies the permissions available at that point into the new thread context. So you cannot, in the simple case, elevate privilege.

Stack walking and permission intersection is slow. It was optimised a lot in Java 9 and 10 so the performance impact of enabling sandboxing is much less than it once was, but it's clearly not zero overhead. Therefore the JVM provides other techniques. One is the notion of a capability, known from many other systems. Instead of doing a permission check on every single file read (slow), do it once and then create a File object. The File object allows reading of the underlying native file via its private fields. Whoever has a pointer to the File object can thus read from it. Because pointers cannot be forged in Java, this is secure as long as you don't accidentally lose your pointer or pass it to code that shouldn't have it.

Sometimes you need to wrap a privileged operation to "dilute" it somehow. For example, imagine you have a module that allows arbitrary socket access. You also have an HTTP client. You would like the HTTP client to have network access, but for it to be usable by other modules that should only be able to contact specific hosts. Given what I've described so far that wouldn't work: the highly privileged code that can do native calls would do a stack walk, discover the unprivileged module on the stack and throw an exception. But there's a fix: AccessController.doPrivileged. This is kind of like sudo. It takes a lambda and truncates the stack that's examined for access checks at the point of use. Therefore it allows a module to use its own assigned permissions regardless of who is calling it. Of course, that is powerful and must be used carefully. In this case the HTTP client would itself check a different, HTTP specific permission. If that permission passed, then it would assert its own power to make arbitrary network connections and go ahead and use the lower level API.

There are a few more pieces but they aren't core. One is the class called SecurityManager. This is the most famous part of the API but in fact, it's no longer really needed. SecurityManager simply delegates to AccessController now. Its API is slightly more convenient for the set of built in permissions. For the purposes of understanding the design you can effectively ignore it. The SecurityManager needs to be activated using a system property as otherwise, for performance reasons, permission checks are skipped entirely at the check sites. Beyond that it can be left alone, or alternatively, customised to implement some unusual security policy. Another piece is the policy language. Permissions are not intrinsic properties of a module in the JVM but rather assigned via an external file. The final piece is the module system. This isn't relevant to the sandbox directly, but it makes it easier to write secure code by adding another layer of protection around code to stop it being accessed by stuff that shouldn't have access to it. After a careful review of the old JVM sandbox escapes from the applet days, the Java team concluded that the module system would have blocked around half of them.

So as you can see the design is very flexible. There's really nothing else like it out there, except maybe .NET CAS but I believe they got rid of that.

Unfortunately there are some pieces missing, if we want to re-awaken this kraken.

The first is that modules have no way to advertise what permissions they need to operate. That has to be specified in an external, per-JVM file, and there are no conventions for exposing this, therefore build tools can't show you permissions or integrate the granting of them.

The second is that some code isn't sandbox compatible. The most common reason for this is that it wants to reflectively break into JVM internals, for example to get better performance. Of course that's not allowed inside a sandbox.

A third is that some code isn't secure when sandboxed because it will, for example, create a File object for your entire home directory and then put it into a global public static field i.e. it doesn't treat its capabilities with care. The module system can help with this because it can make global variables less global, but it's still not ideal.

The final piece is some sort of community consensus that sandboxing matters. Bug reports about sandboxing will today mostly be ignored or closed, because developers don't understand how to use it and don't see the benefit. It's fixable with some better tutorials, better APIs, better tooling etc. But first people have to decide that supply chain attacks are a new thing that matters and can't be ignored any longer.

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#326

This doesn't surprise me. Horrify.. yes. I've noticed more dev teams succumbing to the temptation of easiness that many modern package managers provide (NPM, Cargo, Ivy, etc.) - especially as someone who has to work with offline systems on a regular basis. Because of that ease there are fewer tools and tutorials out there to support offline package management. There are more for using caches, though these are often a…

I disagree: the problem is not that package managers make things easy, it's just that several of them are poorly designed. The fact that pip/npm/gem etc. look for packages in a fallback location if not found in the private repository is a terrible design flaw. One which not all package managers have. For example, when you add a cargo dependency from a private registry, you have to specify the registry that the depend…

Does Cargo resolve transitive dependencies with a hash? So for example, if I have a dependency on tokio (which depends on tokio_core), I don't /think/ the meta-data on tokio forces the exact version of tokio_core on a first download/update?

In which case, would you not get the same issue, if you do the same attack, but with a transitive dependency which you haven't specified?

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#328
post #223

I see a lot of people saying things like "this is why package signing is important" and "we need to know who the developers are" and "we need to audit everything." Some of that is true to some degree, but let me ask you this: why do we consider it acceptable that code you install through a package manager implicitly gets to do anything to your system that you can do? That seems silly! Surely we can do better than tha…

Uh, well the original developers of the Sun JVM didn’t do such a bad job after all when designing it: https://docs.oracle.com/javase/7/docs/technotes/guides/secur...

Javas security manager system, is usually not in effect for the majority of use cases. While maven/grade dependencies, can't run code on installation, generally once the application is ran/tested, it will be with full user permissions, not under a security manager.

The security manager is an additional layer of security that most languages don't have, however Java applets have shown it to be full of holes and generally unsuitable for running untrusted code.

The applet security posture has contributed a great deal towards negative opinion towards the language, probably would have been better off never having existed.

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#329

Earlier quoted context omitted.

>trusted but malicious actors I...think you might have bigger problems going on there. You're tryingto throw a tech solution at a problem that is fundamentally human in nature. That tends to leave nobody satisfied.

Tech solutions are the best solutions when they work! Fighting with your spouse over who does the dishes? Buy a dishwasher! Don’t want your ISP snooping on traffic? Use https / a VPN! Unfortunately, package signing does nothing to protect against the threat vector presented here. The authentication system in npm is working fine. The problem is we put too much trust in software from the internet.

...Hence my classification of it as a human problem. I apologize, this is a quirk of my personal vernacular. This is a problem that emergently arises out of the way human beings interact with each other socially, even before tool use comes into the picture.

Alice has a thing. Bob had a thing that Alice figured would make her life easier so integrates it without looking too hard at it. Alice didn't reallize that by adding Bob's thing, something Alice wanted private was no longer the case even if her primary use case was solved.

The technical solution is making Alice's thing include a really onerous to configure permissions framework that takes the work of getting a thing set up and increases the task list from program thing to program and configure permissions for thing.

The human solution is to realize you don't know Bob from Adam, or his motivations, and to observe what Bob's thing actually does. Then depending on criticality, remake something similar, or actually take the time to get to know Bob and see if he can make what you want for you under some sort of agreement that facilitates good business and trust all around. You can't be sampling for malicious changes in real-time, so it's all about risk management. The issue in our case, is a lot of these projects are essentially gifts with no active attention paid to them after a certain point. It's a variant of cargo cults. You want this thing? Go here, get that, presto. Businesses, developers, (and their exploiters) like that. The price though is that once a project is abandoned, and the rights transferred to someone you don't know, you have to rerun your risk management calculation again.

The thing people should be worried about is all the PHB's (pointy-haired bosses) who just got ammo for their NMIH (Not-Made-In-House) cannons now that supply chain attacks are becoming increasingly visible vectors for attack.

Re: Dependency Confusion: How I Hacked Into Apple, Microsoft and Other Companies

#330
post #44

This post seems like a good time to note that by default, there's no direct way to verify that what you are downloading from dockerhub is the exact same thing that exists on dockerhub [1]. Discovered after seeing a comment on HN about a bill of materials for software, i.e., a list of "approved hashes" to ensure one can audit exactly what software is being installed, which in turn led me to this issue. [1] - https://g…

I think image signing support (or at least was) is not as good as it can be. It would be nice if more images were signed by publishers and verification performed by default. Even then, that only gives you a stronger indication that the image hasn't been altered since it was signed by the image author at any point after it being signed. However it is not a guarantee that the source produced the binary content. It's al…

You can enable client enforcement of Docker Content Trust [1] so that all images pulled via tag must be signed. Whether people are actually signing their images is a different question that I don't know the answer to.

[1] - https://docs.docker.com/engine/security/trust/#client-enforc...

Post reply on HN