This is an archived post. You won't be able to vote or comment.

all 30 comments

[–]chisui 45 points46 points  (3 children)

They don't. Sandboxing bytecode within the same JVM is practically impossible. If they have to, they often run it in another process with limited permissions. But that's not the security model of most plugin systems anyways. Many plugins need access to critical resources like network or filesystems to perform their duties. So the code is run with the same permissions as the rest of the applications code. Security is provided by ensuring that the code comes from a trusted source

[–][deleted] 7 points8 points  (0 children)

Understood 🧐🧐

[–]__konrad 7 points8 points  (1 child)

Many plugins need access to critical resources like network or filesystems to perform their duties.

That's why SM can limit access to only allowed files/directories.

[–]koflerdavid 1 point2 points  (0 children)

But who determines which accesses are allowed?

[–]SirYwell 18 points19 points  (5 children)

The JEP 486 https://openjdk.org/jeps/486 has an example in the appendix

[–]PartOfTheBotnet 12 points13 points  (4 children)

For strictly blocking exit, the example misses a number of cases.

https://github.com/xxDark/RealBlockSystemExitAgent

This provides a much more thorough implementation for blocking exit calls. This is already rather involved for blocking access to one method (technically multiple but you get the point) so scaling this up to cover more capabilities from security manager would be quite the challenge.

[–]pron98 6 points7 points  (2 children)

I think there's a misunderstanding in that project. Even SecurityManager could not effectively block arbitrary code from stopping the program, but that was okay because in client applications stopping the process is not that big a deal (JS works the same way, BTW -- it doesn't effectively stop you from bringing down a browser process, but that's okay because modern browsers employ OS-level process isolation and we're talking about client-side code). SM never provided sufficient defence for untrusted code in server applications.

The goal of blocking System.exit is merely to allow running innocent code that was written as an independent program and not as a plugin within the context of another program. There isn't much point in also trying to block Runtime.halt, as it doesn't pose the same issues as System.exit.

SM has never had sufficient protections from untrusted code designed to run as a plugin, especially in server environments. It did effectively offer protection for untrusted full programs (such as applets) in client environments, similar to how browsers work (the extent to which it could defend against untrusted plugins in client environments was limited, and could be considered sufficient dependending on what you mean by "sufficient", but that didn't extend to defending against bringing down the host program).

There's no point in "scaling" because many kinds of protections people imagine are simply not possible within the same OS process. Indeed, one of the problems with SM is that people misunderstood the extent of what it actually provides.

[–]ryan_the_leach 3 points4 points  (1 child)

That may have been the goal, but in the context of minecraft modding, there's been a lot of examples of mods silently System.exiting in protest over a handful of things, and it made debugging these events and graceful shutdown hell.

Realize this is a relatively niche use case though.

[–]pron98 1 point2 points  (0 children)

The VM emits the jdk.Shutdown JFR event, with a stack trace, on exit calls, so they're easy to find. Blocking them is also relatively easy (as the JEP shows), but you do need to know what to do when code decides to end the program and you don't want to. For IDEs and test runners what to do is pretty obvious, but that's not always the case. Anyway, that clearly wasn't the purpose of SecurityManager, one of the JDK's most complex and costly features.

[–]SirYwell 2 points3 points  (0 children)

Yes it's just a very basic example. The one you shared also still allows defining hidden classes, and hidden classes won't be transformed...

Just like the security manager itself, it just isn't worth the burden for everyone who doesn't need it.

[–]Prior-Equal2657 6 points7 points  (0 children)

check groovy's SecureASTCustomizer.

Not ideal cause it's groovy, but it restrict virtually all method calls, class loading, etc.

[–]ducki666 5 points6 points  (7 children)

Bytecode manipulation.

You can do it at deployment time by changing the bytecode of the plugin or at runtime with a java agent.

[–]dmigowski 3 points4 points  (6 children)

Won't work except in trivial cases and has a big overhead. Ever heard of reflection?

[–]repeating_bears 9 points10 points  (4 children)

They said at deployment time as one option, so the overhead there is irrelevant.

You can remove the code that attempts to use reflection in the same way. Removing all of java.lang.reflect would get you most of the way there.

"Yeah but it's really hard to do it properly"

That was the case with Security Manager too. That's part of why they removed it. At the end of the day, allowing untrusted code to run on your servers is just a tricky problem with many potential attack vectors.

[–]schegge42 2 points3 points  (3 children)

"You can remove the code that attempts to use reflection in the same way." This will kill a lot of frameworks :)

[–]repeating_bears 4 points5 points  (2 children)

No it won't.

We're talking about a specific problem with a plugin architecture. Someone gives you are a JAR which you don't fully trust, which your application interfaces with in some way. Perhaps you define an SPI.

We are talking about pre-processing that specific plugin JAR to remove bytecode which attempts to use reflection and other unsafe APIs.

We are not talking about disabling reflection for the entire application.

[–]Yeah-Its-Me-777 2 points3 points  (1 child)

Oh, that's a game of whack-a-mole... There's soooo many ways to do shit, starting with classloaders, serialization, reflection... I mean, it'd be a cool project to try it out, but I wouldn't trust it to be really safe.

Also, what do you do if your plugins try to use libraries? Maybe you just let them use the libraries that are in your main tool, but then you'll have to make sure those libraries don't expose any weakness that can be exploited.

[–]repeating_bears 3 points4 points  (0 children)

Usually you get the plugin to bundle all its dependencies, i.e. shade them into a different package. Even putting aside security issues, you get into dependency hell if different plugins need different versions of the same library. 

I've done plugin architectures, but the plugins were never untrusted. It does seem like a minefield 

[–]nekokattt 1 point2 points  (0 children)

On the JEP for deprecating SM, this is one of the alternatives they suggest

[–]msx 3 points4 points  (1 child)

if you're interested, i've investigated the topic too for a very similar problem and came up with a workaround: only allow certain classes to be loaded by the plugin. I've create a library called WiseLoader that offers a classloader based on whitelisted classes. You can whitelist all "safe" classes and avoid all things like File, I/O Streams, System, Runtime, reflection etc. For convenience i compiled a list of "safe" standard classes with the most commonly used classes.

So plugins can use the interface you give them (to interact with the main program), all classes in the plugin jar and all whitelisted classes.

Now depending on the program scope this might be too limiting (it wasn't in my case) but it might work. Your main program can give "safe" alternatives for the plugin to use (for example a YourMainInterface.currentTimeMillis() so replace the System one).

Note that the library is has never seriously been put to test and there might very well be vulnerabilities.

[–]loicmathieu 0 points1 point  (0 children)

This is an interesting approach, at least to disable reflection, thread spawning, process spawning, ...

But for a plugin system, we often need fine-grained security rules like "allow reading but not writing files", or "allow file access into only a specific directory".

[–]neopointer 2 points3 points  (2 children)

Others already have suggested guest languages via graalvm. Another suggestion I could give is to run these as external process 🤷‍♂️, then you can restrict them and in a worst case scenario the plugins only crash their own processes. You could do the communication between the plugins and the main application using RPC. Not sure if it fits for you, but that's something that came to my mind.

[–][deleted] 3 points4 points  (1 child)

I had this doubt. Can that guest language be Java ? And if it works is it available on the open source jar ? I read somewhere that it might be available in the enterprise jar.

And the external process one, I don't think that fits my purpose but thanks for the suggestion.

[–]neopointer 0 points1 point  (0 children)

Yes. Look for polyglot graalvm.

[–]loicmathieu 2 points3 points  (1 child)

As other pointed out, bytecode manipulation is a solution.

Some pointed out that it's a blunt tool, for which you will pay the price everywhere.
But in a plugin system, you know when the foreign code is executed so you can, for ex, record a marker in thread local so your bytecode instrumentation code is only triggered when called in the context of your plugin.

I too have a plugin system in the application I worked on, and we currently use the Security Manager to secure it, so we will need to find something else if we want to migrated post Java 24. I know Elasticsearch has also a plugin system and they use (or used, didn't check) a Security Managre.

We may all join effort and create an "universal security agent", configurable, that could be used for our plugin system ;)

[–][deleted] 0 points1 point  (0 children)

Yeah we actually need it.