Does AccessController.doPrivileged give JavaScript

2019-03-25 07:10发布

问题:

I'm looking at a signed Applet that is heavily called from JavaScript. Obviously, the threads that originate from JavaScript are more heavily sandboxed than any thread started directly from within Java. For example, if a JavaScript thread calls into the Applet and logs something that causes the log file to roll, a security exception is thrown. Any thread started directly within the Applet will not experience this security exception. The solution here with log4j is to use the asynchronous appender.

But with other security exceptions (for example making use of Apache Axis in the signed Applet but in a JavaScript thread) there is no obvious way to have some asynchronous thread. Let's say I have the following code that if called from a Java thread will work and if called via JavaScript will fail with a SecurityException:

public void someMethodCalledFromJavaScript() {
  // Stuff that would throw a SecurityException
}

I see three following options, but they may not all be valid. For the sake of this discussion, ignore whether or not the execution will be synchronous or asynchronous, as that's easily managed. I am having a difficult time understanding the details of the security model. Here are my three potential choices:

  • Start a new Thread (will this one even work?):

    public void someMethodCalledFromJavaScript() {
      new Thread(new Runnable() {
        public void run() {
          // Stuff that would throw a SecurityException
        }
      }).start();
    }
    
  • Have the Applet have a thread ready to go at all times, triggered via the JavaScript-origin thread (highly simplified code here):

    private volatile boolean doit = false;
    
    // This code is running in a Thread, started @ Applet init time
    public void alwaysWaiting() {
      while (true) {
        if (doit) {
          doit = false;
          // Stuff that would throw a SecurityException
        }
      }
    }
    
    public void someMethodCalledFromJavaScript() {
      doit = true;
    }
    
  • Use AccessController.doPrivileged:

    public void someMethodCalledFromJavaScript() {
      AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
          // Stuff that would throw a SecurityException
          return null;
        }
      });
    }
    

According to what I read of AccessController.doPrivileged, you run with the intersection of the current security privs and the privs of the security domain of the code you're calling. This doesn't make sense to me, as if you're running with the intersection of a low and a high security domain, you'll just have the low security domain. So clearly I'm not understanding something.

The specific SecurityException I'm seeing is this one:

java.security.AccessControlException: access denied (java.lang.RuntimePermission accessDeclaredMembers)

but of course I'm curious about the general case in the context of JavaScript calling into a signed Applet, and how I can allow a JavaScript-originated thread to run with the priv's of the signed Applet as if it were a thread that originated purely within the Applet.

Which choices above will even work, and which are better than others, and why.

回答1:

  • "Start a new Thread (will this one even work?)"

Wont work because of reasons mentioned below

  • Have the Applet have a thread ready to go at all times, triggered via the JavaScript-origin thread

Will work of course, but that's more painful than calling doPrivileged, yet has the same effect semantically.

  • Use AccessController.doPrivileged

Yes this will work.

Every access control check inspects the set of all stack frames on the stack of the current thread (including the stack frame leading up to the instantiation of the current thread, recursively). If there is a doPrivileged frame, frames leading up to that frame are not included in the set (but the actual doPrivileged frame is included).

If the privilege being checked is not in every single frame in that set, the check fails.

In other words, the current privileges of a thread are the intersection of privileges in this set.

So for example if privileged code doPrivilegeds some unprivileged code which tries to open a file, the check will fail. Likewise if unprivileged code doPrivilegeds privileged code that opens a file, the check will fail. But if unprivileged code invokes privileged code and the privileged code in turn calls doPrivileged to open a file, the check will succeed.

In theory, you should be able to only grant your Java codebase the privileges it needs (perhaps access to some isolated directory), and then grant the same privileges to the JavaScript code that will use this privileged code, but I doubt any browser has such features. I'm surprised JavaScript even runs in another protection domain than Java.

I've never done JavaScript<->Java interop, but it seems no matter what you are going to have to make the methods that are invoked by JavaScript use doPrivileged blocks on their entire body.


EDIT: As Sami said, be careful when invoking doPrivileged blocks within the privileged code (and read his answer).



回答2:

I'd go with the doPrivileged. Just be aware that everybody that has access to your applet can download it and put it on their site and have their own malicious javascript call it in ways you didn't imagine.

The security implications of the other solutions are pretty much the same (EDIT: although creating a new Thread doesn't work, as Longpoke pointed out), but they are more complicated. So I don't see any advantage with them.

The AccessController.doPrivileged considers the protection domain of just the immediate caller. In your example, the class where your method someMethodCalledFromJavaScript is defined. If this a trusted class in the signed jar, it doesn't matter that the untrusted Javascript is calling it.