Java SecurityManager - how to ensure a method is r

2019-10-04 16:10发布

问题:

I want B to be run only by the private method A#getSensitiveData() that uses or does some processing on sensitive data (example: cryptographic keys, national id, whatever).

public final class A{
    private transient final B sensitiveHolder; //set at Constructor
    public A(B sensitiveHolder){
        this.sensitiveHolder = sensitiveHolder;
    }
    private final byte[] getSensitiveData(){
        return sensitiveHolder.getSensitiveData();
    }
}

public final class B{
    private transient final byte[] sensitiveData;//encrypt and set at Constructor
    public final byte[] getSensitiveData(){
        //check if it is run by A#getSensitiveData(); if it is, decrypt by DEK and give plaintext.
    }
}

Please take into account that the code would be obfuscated, so please refrain from putting in any package names as String.

What must I write with SecurityManager#checkPrivilege() and AccessController.doPrivileged() before I can achieve such an effect?

EDIT: Obviously this is different because the so called "answer" does not contain any CODE. WORKING CODE is worth infinitely more than "oh, just do this and that".

回答1:

You could do something like this:

private boolean verify(final StackTraceElement e[]) {
    boolean doNext = false;
    for (final StackTraceElement s : e) {
        if (doNext && s.getClassName().equals("A") && s.getMethodName().equals("getSensitiveData"))
            return true;
        doNext = s.getMethodName().equals("getStackTrace");
    }
    return false;
}

And to call the method:

public final byte[] getSensitiveData(StackTraceElement e[]){
    if (verify(e)) {
        // Do something good
    }
}

In your A class call your B class like this:

return sensitiveHolder.getSensitiveData(Thread.currentThread().getStackTrace());

I don't know if this is what you need or it is near that. You could play around the values in the equals section of the if. I got and modified the example from this site.



回答2:

If you're able to use JDK 9+, which introduces StackWalker, this sort of thing might work for you. This technique seems to supersede use of sun.reflect.Reflection#getCallerClass(int). (I hope you weren't counting on a SecurityManager-related answer.)

package asdf;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;

public class Asdf {

  @Test
  public void what() {
    get();
  }

  void get() {
    StackWalker.StackFrame stackFrame =
        StackWalker.getInstance(EnumSet.of(StackWalker.Option.RETAIN_CLASS_REFERENCE))
            .walk(stream -> {
              List<StackWalker.StackFrame> stackFrames = stream.collect(Collectors.toList());
              return stackFrames.get(1);
            });
    Assertions.assertEquals(Asdf.class, stackFrame.getDeclaringClass());
    Assertions.assertEquals("what", stackFrame.getMethodName());
    Assertions.assertEquals(0, stackFrame.getMethodType().parameterCount());

    // now do caller-sensitive stuff
  }
}


标签: java security