Live data from Hacker News

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

medium.com

381–390 of 412 posts

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

#381

Earlier quoted context omitted.

That's the same thing I was just describing but recursed another level. It doesn't help by itself. Something needs to have permission to use the higher level of privilege - raw network access in my example, 'raw' http client access in yours. And something else needs to check that permission. Yes, you could wrap that privilege in a capability afterwards, but the reason Java has both capabilities and stack walking is b…

Maybe I'm missing something about the use case, but I'm not sure I quite follow. Sure, something needs to have permission to use the higher level of privilege. On your typical POSIX OS, your program is probably born with the ability to create arbitrary TCP/UDP sockets by default; on a capability OS, maybe you've explicitly provided it with access to your network stack. Regardless, at the entry point to your program y…

You can build a sandboxing language without any sort of stack walking. SEL4+C does this. It doesn't have especially good usability at scale, and it's not easy to modularise.

You're imagining a system where there's no specific authentication system for code. Instead in order to use a library, you need to explicitly and manually obtain all the capabilities it needs then pass them in, and in main() you get a kind of god object that can do everything that then needs to be progressively wrapped. If a library needs access to a remote service, you have to open the socket yourself and pass that in, and the library then needs to plumb it through the whole stack manually to the point where it's needed. If the library develops a need for a new permission then the API must change and again, the whole thing has to be manually plumbed through. This is unworkable when you don't control all the code in question and thus can't change your APIs, and as sandboxing is often used for plugins, well, that's a common problem.

There's no obvious way to modularise or abstract away that code. It can't come from the library itself because that's what you're trying to sandbox. So you have to wire up the library to the capabilities yourself. In some cases this would be extremely painful. What if the library in question is actually a networking library like Netty? There could be dozens or hundreds of entry points that eventually want to open a network connection of some sort.

What does this god object look like? It would need to hold basically the entire operating system interface via a single access point. That's not ideal. In particular, loading native code would need to also be a capability, which means any library that optimised by introducing a C version of something would need to change its entire API, potentially in many places. This sort of design pattern would also encourage/force every library to have a similar "demi-god" object approach, to reduce the pain of repeatedly passing in or creating capabilities. Sometimes that would work OK, other times it wouldn't.

The stack walking approach is a bit like SELinux. It allows for a conventional OO class library, without the need for some sort of master or god object, and all the permissions things need can be centralised in one place. Changes to permissions are just one or two extra lines in the security config file rather than a potentially large set of code diffs.

Now all that said, reasonable people can totally disagree about all of this. The JVM has been introducing more capability objects with time. For example the newer MethodHandle reflection object is a capability. FileChannel is a capability (I think!). You could build a pure capability language that runs on the JVM and maybe someone should. Perhaps the usability issues are not as big a deal as they seem. It would require libraries to be wrapped and their APIs changed, including the Java standard library, but the existing functionality could all be reused. The new libraries would just be a thin set of wrappers and forwarders over pre-existing functionality, but there'd be no way for anything except the god object to reach code that'd do a stack walk. Then the security manager can be disabled, and no checks will occur. It'd be a pure object capability approach.

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

#382

What I don’t get from the article is the reasoning behind the design that the central repository “wins” over the local/override repository. How was that design chosen, not just once but in all 3 of those large package ecosystems. Did pypi/gems/node borrow their design from each other given their similarity in other aspects? Are there any situations where this behavior is desired? Does any of the other ecosystems have…

Cargo will not ever look on crates.io for a library if you specify a `registry` attribute for a dependency. https://doc.rust-lang.org/cargo/reference/registries.html

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

#383

Earlier quoted context omitted.

The File example is a good illustration of why Java is _not_ a capability-secure language. Every File object in Java has a getParentFile() method that allows you to navigate up the hierarchy right to the root and then from there access every file on the filesystem. Java’s standard library is full of these kinds of design flaws. So in practice you can only apply capability-based thinking to small subsets of a codebase…

I shouldn't have used File as an example, that was confusing. I was trying to explain capabilities and stack walking in an abstract sense but was also using Java as a concrete example. Big mistake. You're right that java.io.File isn't a capability. It just represents a file path with a few utility methods to list files, and therefore does a stack walk when you try to access the filesystem. A FileChannel is a file cap…

Are you aware of the history of object-capability programming languages? There are multiple actual demonstrations of real-world ocaps programming languages and projects built with them:

* The (now defunct) E programming language: http://erights.org and Joule: https://en.wikipedia.org/wiki/Joule_(programming_language) * Pony: https://www.ponylang.io * Monte: http://www.monte-language.org * The Midori project at Microsoft: http://joeduffyblog.com/2015/11/10/objects-as-secure-capabil...

It's actually not at all unworkable to use object-capability for large programs. In fact, one of the main benefits of ocaps is how well it aligns with well-established good software design principles such as dependency injection, avoiding singletons, avoiding global mutable state, and so on.

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

#384

Earlier quoted context omitted.

Maybe I'm missing something about the use case, but I'm not sure I quite follow. Sure, something needs to have permission to use the higher level of privilege. On your typical POSIX OS, your program is probably born with the ability to create arbitrary TCP/UDP sockets by default; on a capability OS, maybe you've explicitly provided it with access to your network stack. Regardless, at the entry point to your program y…

You can build a sandboxing language without any sort of stack walking. SEL4+C does this. It doesn't have especially good usability at scale, and it's not easy to modularise. You're imagining a system where there's no specific authentication system for code. Instead in order to use a library, you need to explicitly and manually obtain all the capabilities it needs then pass them in, and in main() you get a kind of god…

> If a library needs access to a remote service, you have to open the socket yourself and pass that in, and the library then needs to plumb it through the whole stack manually to the point where it's needed.

You don't need to do this. There are a variety of ways to handle this, just as you would any other kind of dependency injection:

1. Design libraries to actually be modular so that dependencies (including capabilities) can be injected just where they are needed.

2. Pass in a factory object that lets the library construct sockets as and when it needs them. You can then enforce any arbitrary checks at the point of creating the socket. (This is much more flexible than a Java policy file).

3. Use a powerbox pattern [1] to allow the user to be directly asked each time the library attempts to open a socket. This is not always good UX, but sometimes it is the right solution.

> If the library develops a need for a new permission then the API must change and again, the whole thing has to be manually plumbed through.

Capturing permission requirements in the API is a good thing! With the stack walking/policy based approach I won't know the library needs a new permission until some library call suddenly fails at runtime.

[1]: http://wiki.erights.org/wiki/Powerbox

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

#386
This is both a security bug and a reproducibility bug. If anyone outside your network can break your build, your build is broken! It's mission critical to have a working build.

The way Nix handles this is that every external resource is cached and hashed, and every reference to an external resource must have a hash integrity check. If someone swaps out a package on a web server somewhere, rebuilds keep working because they don't need to re-fetch (because the hash wasn't changed by an operator), and fresh builds fail with an error indicating the hash is invalid, which should trigger an investigation (in practice, this is exceedingly rare, and IMO always deserves attention).

I dream for when build reproducibility is considered table stakes like version control.

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

#387

Earlier quoted context omitted.

I shouldn't have used File as an example, that was confusing. I was trying to explain capabilities and stack walking in an abstract sense but was also using Java as a concrete example. Big mistake. You're right that java.io.File isn't a capability. It just represents a file path with a few utility methods to list files, and therefore does a stack walk when you try to access the filesystem. A FileChannel is a file cap…

Are you aware of the history of object-capability programming languages? There are multiple actual demonstrations of real-world ocaps programming languages and projects built with them: * The (now defunct) E programming language: http://erights.org and Joule: https://en.wikipedia.org/wiki/Joule_(programming_language) * Pony: https://www.ponylang.io * Monte: http://www.monte-language.org * The Midori project at Micros…

I know about E and Midori. I haven't looked at the others. As far as I know the only one that could realistically be said to have been used for large programs was Midori but very little about it was ever published, just a few blog posts. And Midori was cancelled. Presumably it wasn't so compelling.

I'd like to see a more modern attempt that wasn't as totally obscure as those other languages. However, nobody is doing that.

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

#388

Earlier quoted context omitted.

You can build a sandboxing language without any sort of stack walking. SEL4+C does this. It doesn't have especially good usability at scale, and it's not easy to modularise. You're imagining a system where there's no specific authentication system for code. Instead in order to use a library, you need to explicitly and manually obtain all the capabilities it needs then pass them in, and in main() you get a kind of god…

> If a library needs access to a remote service, you have to open the socket yourself and pass that in, and the library then needs to plumb it through the whole stack manually to the point where it's needed. You don't need to do this. There are a variety of ways to handle this, just as you would any other kind of dependency injection: 1. Design libraries to actually be modular so that dependencies (including capabili…

The policy file isn't required, by the way. That's just a default implementation. My PDF viewer had a hard-coded policy and didn't use the file.

OK, so in a pure capability language how would you implement this: program A depends on dynamically loaded/installed plugin B written by some third party, that in turn depends on library C. One day library C gets a native implementation of some algorithm to speed it up. To load that native library requires a capability, as native code can break the sandbox. However:

1. You can't change the API of C because plugin B depends on it and would break.

2. You can't pass in a "load native library" capability to plugin B because you don't know in advance that B wants to use C, and if you did, B could just grab the capability before it gets passed to C and abuse it. So you need to pass the capability directly from A to C. But now A has to have a direct dependency on C and initialise it even if it's not otherwise being used by A or B.

Stack walking solves both these problems. You can increase the set of permissions required by library C without changing its callers, and you don't have the problem of needing to short-circuit everything and create a screwed up dependency graph.

With the stack walking/policy based approach I won't know the library needs a new permission until some library call suddenly fails at runtime

You often wouldn't need to. What permissions a module has is dependent on its implementation. It's legitimate for a library to be upgraded such that it needs newer permissions but that fact is encapsulated and abstracted away - just like if it needed a newer Java or a newer transitive dependency.

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

#390

Earlier quoted context omitted.

> If a library needs access to a remote service, you have to open the socket yourself and pass that in, and the library then needs to plumb it through the whole stack manually to the point where it's needed. You don't need to do this. There are a variety of ways to handle this, just as you would any other kind of dependency injection: 1. Design libraries to actually be modular so that dependencies (including capabili…

The policy file isn't required, by the way. That's just a default implementation. My PDF viewer had a hard-coded policy and didn't use the file. OK, so in a pure capability language how would you implement this: program A depends on dynamically loaded/installed plugin B written by some third party, that in turn depends on library C. One day library C gets a native implementation of some algorithm to speed it up. To l…

Typically libraries don’t directly load one another. The language runtime does this.
Post reply on HN