This week's book giveaway is in the OO, Patterns, UML and Refactoring forum. We're giving away four copies of Refactoring for Software Design Smells: Managing Technical Debt and have Girish Suryanarayana, Ganesh Samarthyam & Tushar Sharma on-line! See this thread for details.
I am developing an enterprise-class application in which users could specify an action to be executed in response to specific events.
I would like to allow users to be able to script the response as a groovy script. While this seems an appropriate application of groovy, my concern is about security: how can I prevent a malicious user from subverting the application's JVM using a suitable groovy script?
Could I install a security manager which can detect operations initiated from a groovy script (as opposed to those initiated from the main application)?
GULP! Allowing executable code from an end-user -- in Java, Groovy, or any other language -- is an exercise for someone (anyone!) braver than me. I'm going to ask all hackers to avert their eyes for a moment.
Groovy allows you to wrap anything that you'd normally type at the command line in quotes and call .execute() on it. It's a convenience method that creates a java.lang.Process behind the scenes.
I'd find ANY OTHER POSSIBLE WAY to do what you are trying to accomplish before I'd let someone submit code to my system without my prior knowledge. The only way I'd even vaguely consider doing something like that is writing a DSL (Domain Specific Language) that had such a bone-simple syntax that I could scan it for bad things, but even then I wouldn't sleep very well at night... Those hackers are a smart, tenacious bunch, and have waaay more time than I do to think of nefarious ways to abuse innocent systems.
Well, since Groovy runs in a JVM, I'd think it could use the JVM's SecurityManager same as any Java program. So we could use a SecurityManager set with the same permissions that are used for, say, any untrusted applet found on the web. This would disallow file access, and Runtime.exec(), and various other things I don't know about. That should block any access to Groovy's execute() method, which doubtless uses Runtime.exec() internally.) Then you could use GroovyShell.evaluate() or Eval.me() methods to evaluate some string the user passed in and obtain a result. Should be as safe as running a web browser with Java enabled, no?
A problem with this approach as described is that we may well want access to the file system or Runtime.exec() (or Groovy's execute()) ourselves. So we'd want one security manager for our own program, and another for evaluating stuff from users. This would seem to require two different JVMs, since a single JVM can only have one security manager at a time (as I understand it). I suppose you could use Runtime.exec() to start a second JVM with a more restrictive set of permissions, as a Process. Then pass it the user's script using Process.getInputStream(), and obtain the result using PRocess.getOutputStream(). With a bit of care, you could probably set this up so you only have to start the second JVM once, then keep it running indefninitely while you pass it a series of commands, and get a series of responses. Perhaps wrap these with some simple XML, or use object serialization, so that you have an easy way to tell when one response is completed and the next begins.
Well, maybe Uma will be able to sleep at night with that solution, so you would have gotten a two out of two, which is great. Frankly, I cannot imagine myself giving the users the ability to enter their code into the system. I believe that your solution is the best that can be done, and I believe that if configured correctly, it'll be very secure. But if I do it, I believe that I'll be getting a lot of nightmares about what I might have forgotten, and what users may do to the system. OUCH!