How do you forbid users from doing evil things in Groovy Scripts?

后端 未结 4 425
渐次进展
渐次进展 2021-01-22 06:17

I\'m planning to integrate Groovy Script Engine to my game so it will give the game nice moddability but how do you prevent players from writing evil scripts like deleting all f

相关标签:
4条回答
  • 2021-01-22 06:41

    There's a blog post by Cedric Champeau on customising the Groovy Compilation process, the second part of it shows how to use SecureASTCustomizer and CompilerConfiguration to limit what Scripts can do (and then has examples of defining your own AST checks for System.exit, etc...

    0 讨论(0)
  • 2021-01-22 06:43

    The Groovy Web Console appears to have already solved this problem, because it won't execute something like System.exit(1). The source code is available on GitHub, so you can see how they did it.

    If you're not sure where to start, I suggest getting in touch with the author, who should be able to point you in the right direction.

    0 讨论(0)
  • 2021-01-22 06:44

    I know this is a old question. I'm posting this as it might help some people out there.

    We needed to allow end-users to upload Groovy scripts and execute them as part of a web application (that does a lot of other things). Our concern was that within these Groovy scripts, some users might attempt to read files from the file system, read System properties, call System.exit(), etc.

    I looked into http://mrhaki.blogspot.com/2014/04/groovy-goodness-restricting-script.html but that will not prevent an expert Groovy developer from bypassing the checks as pointed out by others in other posts.

    I then tried to get http://www.sdidit.nl/2012/12/groovy-dsl-executing-scripts-in-sandbox.html working but setting the Security Manager and Policy implementation at runtime did not work for me. I kept running into issues during app server startup and web page access. It seemed like by the time the Policy implementation took hold, it was too late and "CodeSources" (in Java-Security-speak) already took its access settings from the default Java policy file.

    I then stumbled across the excellent white paper by Ted Neward (http://www.tedneward.com/files/Papers/JavaPolicy/JavaPolicy.pdf) that explained quite convincingly that the best approach (for my use case) was to set the Policy implementation on JVM startup (instead of dynamically later on).

    Below is the approach that worked for me (that combines Rene's and Ted's approaches). BTW: We're using Groovy 2.3.10.

    In the [JDK_HOME]/jre/lib/security/java.security file, set the "policy.provider" value to "com.yourcompany.security.MySecurityPolicy".

    Create the MySecurityPolicy class:

    import java.net.MalformedURLException;
    import java.net.URL;
    import java.security.AllPermission;
    import java.security.CodeSource;
    import java.security.PermissionCollection;
    import java.security.Permissions;
    import java.security.Policy;
    import java.util.HashSet;
    import java.util.Set;
    
    public class MySecurityPolicy extends Policy {
        private final Set<URL> locations;
    
        public MySecurityPolicy() {
            try {
                locations = new HashSet<URL>();
                locations.add(new URL("file", "", "/groovy/shell"));
                locations.add(new URL("file", "", "/groovy/script"));
            } catch (MalformedURLException e) {
                throw new IllegalStateException(e);
            }
        }
    
        @Override
        public PermissionCollection getPermissions(CodeSource codeSource) {
            // Do not store these in static or instance variables. It won't work. Also... they're cached by security infrastructure ... so this is okay.
            PermissionCollection perms = new Permissions();
            if (!locations.contains(codeSource.getLocation())) {
                perms.add(new AllPermission());
            }
            return perms;
        }
    }
    

    Jar up MySecurityPolicy and drop the jar in [JDK_HOME]/jre/lib/ext directory.

    Add "-Djava.security.manager" to the JVM startup options. You do not need to provide a custom security manager. The default one works fine.

    The "-Djava.security.manager" option enables Java Security Manager for the whole application. The application and all its dependencies will have "AllPermission" and will thereby be allowed to do anything.

    Groovy scripts run under the "/groovy/shell" and "/groovy/script" "CodeSources". They're not necessarily physical directories on the file system. The code above does not give Groovy scripts any permissions.

    Users could still do the following:

    • Thread.currentThread().interrupt();

    • while (true) {} (infinite loop)

    You could prepend the following (dynamically at runtime) to the beginning of every script before passing it onto the Groovy shell for execution:

    @ThreadInterrupt
    import groovy.transform.ThreadInterrupt
    
    @TimedInterrupt(5)
    import groovy.transform.TimedInterrupt
    

    These are expalined at http://www.jroller.com/melix/entry/upcoming_groovy_goodness_automatic_thread

    The first one handles "Thread.currentThread().interrupt()" a little more gracefully (but it doesn't prevent the user from interupting the thread). Perhaps, you could use AST to prevent interupts to some extent. In our case, it's not a big issue as each Groovy script execution runs in its own thread and if bad actors wish to kill their own thread, they could knock themselves out.

    The second one prevents the infinite loop in that all scripts time out after 5 seconds. You can adjust the time.

    Note that I noticed a performance degradation in the Groovy script execution time but did not notice a significant degradation in the rest of the web application.

    Hope that helps.

    0 讨论(0)
  • 2021-01-22 06:49

    Look into the SecurityContext class.

    0 讨论(0)
提交回复
热议问题