Permission für eine bestimmte Klasse oder Package

Vincentius

Erfahrenes Mitglied
Hallo zusammen,

ich versuche ein paar Methoden einer Klasse von Zugriffen zu schützen und nur einer bestimmten Klasse oder einem bestimmten Package erlauben, auf die Methoden zuzugreifen. Dafür habe ich eine eigene Permission Klasse angelegt und sie in der ".policy" Datei deklariert:

Code:
grant {
    permission com.xyz.security.MyPermission "badMethod";
};

Die MyPermission Klasse ist von der java.security.BasicPermission abgeleitet und überschreibt lediglig den Konstruktor:
Code:
public class MyPermission extends BasicPermission {

    public MyPermission(String name) {
        super(name);
    }
}

In der Klasse mit der Methode, die ich zu schützen versuche, rufe ich den SecurityManager auf um die Permission zu prüfen:
Code:
public final class BadClass {
    public static void badMethod() {
        checkPermission();
        // do bad things...
    }
    /**
     * Checks if the invoker of this class has required permission.
     */
    private static void checkPermission() {
        final SecurityManager manager = System.getSecurityManager();
        if (manager != null) {
            manager.checkPermission(new MyPermission("badMethod"));
        }
    }
}
Der SecurityManager wird natürlich beim Programmstart aktiviert mit -Djava.security.manager und meine ".policy" Datei übergeben: -Djava.security.policy=.java.policy

Jetzt komme ich zu meinem eigentlichen Problem:
wenn ich die grant Anweisung in der ".policy" Datei so lasse, kann jede Klasse auf meine "badMethod" zugreifen. Der Zugriff soll aber nur bestimmten Klassen oder bestimmten Packages gewährt werden. Wie und wo kann ich das festlegen? Etwa mit codeBase in der grant Anweisung? Aber damit kann man lediglich eine URL festlegen, von der der Code stammt, aber keine Packages oder Klassen-Namen.

Hat jemand vielleicht eine Idee? Vielleicht mache ich grundsätzlich was flasch?
Über jede Hilfe wäre ich sehr dankbar!

Grüße
Vincent
 
Moin!
Ob das mit einer Policy File zu regeln geht, kann ich dir nicht sagen.

Als alternative könntest du aber programmatisch die aufrufende Klasse ermitteln, und dann entscheiden, ob der Zugriff gestattet ist.
Code:
Class<?> caller = sun.reflect.Reflection.getCallerClass(2);

Das sun.reflect Package müsste sich imho im rt.jat befinden..

*grüssle*
MeinerEiner
 
Ich hole mir die aufrufende Methode und Klasse aus nem Stacktrace und entscheide dann ob der Aufruf erlaubt ist oder nicht. Funktioniert auch sehr gut :)
 
Hallo,

danke Euch beiden für die Vorschläge!
Problem ist noch, dass die zu schützende Klasse gar nicht "weiss", wer sie aufrufen darf. Das soll eben nachträglich über ".policy" Datei festgelegt werden können, damit der Code der Klasse nicht mehr angefasst werden muss. Der Grund ist, das die Klasse "BadClass" ein Bestandteil einer API ist, die als wiederverwendbare Komponente in verschiedenen Projektenbenutzt werden soll. Deswegen muss sie so generisch wie möglich bleiben.

Grüße
Vincent
 
Kannst du nicht eine art "Klassen-Whitelist" in eine .properties-Datei (oder irgend was eigenes, was du dann sogar verschlüsseln könntest) schreiben. Darauf kannst du zur Laufzeit aus deinem Programm heraus zugreifen.
dann kannst du das so machen wie Zeja das vorgeschlagen hat?!

Grüße
Daniel
 
Hallo Laocoon,

ja, das würde bestimmt gehen. Nur hat mir mein Chef aufgetragen, dieses Problem unbedingt mit java.security zu lösen, denn genau dafür ist es da, meint er. Leider habe ich bis jetzt keine geeignete Verwendung für Security Package entdeckt, um dieses Problem zu lösen...

Grüße
Vincent
 
Hallo,

es gibt am SecurityManager die Methode checkPackageAccess:
System.getSecurityManager().checkPackageAccess("de.tutorials.secure.internal");
dabei wird dan geprüft ob du ne RuntimePermission für permission java.lang.RuntimePermission "accessClassInPackage.de.tutorials.services.secure.internal";
hast. Damit könnte man vielleicht auch so einen Mechanismus bauen...

...ich würde das einfach über einen statischen AspectJ Aspect machen:

Mein ISecureService:
Java:
/**
 * 
 */
package de.tutorials.services.secure;

/**
 * @author Thomas.Darimont
 *
 */
public interface ISecureService{
	void secureOperation1();
	void secureOperation2();
	void secureOperation3();
}

Mein SecureService:
Java:
/**
 * 
 */
package de.tutorials.services.secure.internal;

import de.tutorials.services.secure.ISecureService;

/**
 * @author Thomas.Darimont
 *
 */
public class SecureService implements ISecureService{

	public void secureOperation1() {
		System.out.println("secureOperation1");
	}

	public void secureOperation2() {
		System.out.println("secureOperation2");
	}

	public void secureOperation3() {
		System.out.println("secureOperation3");
	}

}

Meine Permission:
Java:
/**
 * 
 */
package de.tutorials.security.permissions;

import java.security.BasicPermission;

/**
 * @author Thomas.Darimont
 * 
 */
public class MethodAccessPermission extends BasicPermission {
	public MethodAccessPermission(String methodPermissionPattern) {
		super(methodPermissionPattern);
	}
}


Mein CodeAccessSecurity Aspect:
Java:
package de.tutorials.aspects;

import de.tutorials.security.permissions.MethodAccessPermission;

public aspect CodeAccessSecurity {

	pointcut methodExecutionInSecurePackage() : execution(* de.tutorials.services.secure..*.*(..));

	before(): methodExecutionInSecurePackage(){
		System.out.println(thisJoinPointStaticPart.getSignature()
				.toLongString());

		checkAccess(thisJoinPointStaticPart.getSignature().toLongString());
	}

	final void checkAccess(String methodSignature) {
		String callingClassName = new Exception().getStackTrace()[3]
				.getClassName(); //dynamic cflow would be better...
		System.getSecurityManager().checkPermission(
				new MethodAccessPermission(callingClassName + " -> "
						+ methodSignature));
	}
}

Das Policy File:
Code:
grant {
    permission de.tutorials.security.permissions.MethodAccessPermission "de.tutorials.SecureCodeAccessExample -> public void de.tutorials.services.secure.internal.SecureService.secureOperation1()";
    permission de.tutorials.security.permissions.MethodAccessPermission "de.tutorials.SecureCodeAccessExample -> public void de.tutorials.services.secure.internal.SecureService.secureOperation3()";
};

Der test:
Java:
/**
 * 
 */
package de.tutorials;

import de.tutorials.services.secure.ISecureService;
import de.tutorials.services.secure.internal.SecureService;

/**
 * @author Thomas.Darimont
 * 
 */
public class SecureCodeAccessExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.setSecurityManager(new SecurityManager());
		ISecureService secureService = new SecureService();
		try {
			secureService.secureOperation1();
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
		
		try {
			secureService.secureOperation2();
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
		
		try {
			new InsecureCodeAccessExample().callSecureOperation3MethodOn(secureService);
			
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
		
		try {
			secureService.secureOperation3();
		} catch (Throwable throwable) {
			throwable.printStackTrace();
		}
	}

}

Java:
/**
 * 
 */
package de.tutorials;

import de.tutorials.services.secure.ISecureService;

/**
 * @author Thomas.Darimont
 *
 */
public class InsecureCodeAccessExample {
	public void callSecureOperation3MethodOn(ISecureService secureService) {
		secureService.secureOperation3();
	}
}

Starten mit -Djava.security.policy=.policy

Ausgabe:
Code:
public void de.tutorials.services.secure.internal.SecureService.secureOperation1()
secureOperation1
public void de.tutorials.services.secure.internal.SecureService.secureOperation2()
public void de.tutorials.services.secure.internal.SecureService.secureOperation3()
java.security.AccessControlException: access denied (de.tutorials.security.permissions.MethodAccessPermission de.tutorials.SecureCodeAccessExample -> public void de.tutorials.services.secure.internal.SecureService.secureOperation2())
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
	at java.security.AccessController.checkPermission(AccessController.java:546)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
	at de.tutorials.aspects.CodeAccessSecurity.checkAccess(CodeAccessSecurity.aj:19)
	at de.tutorials.aspects.CodeAccessSecurity.ajc$before$de_tutorials_aspects_CodeAccessSecurity$1$90d03b4(CodeAccessSecurity.aj:13)
	at de.tutorials.services.secure.internal.SecureService.secureOperation2(SecureService.java:19)
	at de.tutorials.SecureCodeAccessExample.main(SecureCodeAccessExample.java:28)
java.security.AccessControlException: access denied (de.tutorials.security.permissions.MethodAccessPermission de.tutorials.InsecureCodeAccessExample -> public void de.tutorials.services.secure.internal.SecureService.secureOperation3())
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
	at java.security.AccessController.checkPermission(AccessController.java:546)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
	at de.tutorials.aspects.CodeAccessSecurity.checkAccess(CodeAccessSecurity.aj:19)
	at de.tutorials.aspects.CodeAccessSecurity.ajc$before$de_tutorials_aspects_CodeAccessSecurity$1$90d03b4(CodeAccessSecurity.aj:13)
	at de.tutorials.services.secure.internal.SecureService.secureOperation3(SecureService.java:23)
	at de.tutorials.InsecureCodeAccessExample.callSecureOperation3MethodOn(InsecureCodeAccessExample.java:14)
	at de.tutorials.SecureCodeAccessExample.main(SecureCodeAccessExample.java:34)
public void de.tutorials.services.secure.internal.SecureService.secureOperation3()
secureOperation3

Gruß Tom
 
Danke Thomas!

Habe es jetzt zwar ohne AspectJ gemacht, aber Deine Idee aufgegriffen, den Klassen-Namen im Konstruktor der Permission-Klasse zu übergeben. Das einzige, was mir nicht so sauber erscheint, ist das Ermitteln der Aufrufer-Klasse über den StackTrace einer Exception. Aber anders geht's wohl leider nicht.

Vielen Dank auch an alle anderen!

Grüße
Vincent
 
Hallo,

Habe es jetzt zwar ohne AspectJ gemacht, aber Deine Idee aufgegriffen, den Klassen-Namen im Konstruktor der Permission-Klasse zu übergeben. Das einzige, was mir nicht so sauber erscheint, ist das Ermitteln der Aufrufer-Klasse über den StackTrace einer Exception. Aber anders geht's wohl leider nicht.
Jo das ermitteln der Aufrufer Klasse ist nicht gerade sehr elegant und obendrein noch Fehleranfällig. Würdet ihr beispielsweise AspectJ noch für andere Aspekte einsetzen oder wird der Service beispielsweise noch über einen Dynamic Proxy gewrapped (JDK DynamicProxy oder CGLib Proxy) so ändert sich der Callstack und somit stimmt der Index mit dem ich in die Exception (StackTrace) schaue nicht mehr. Was man hier tun müsste ist sich irgendwie selbst den Aufrufstack zu merken oder mit genau dafür gedachten Techniken (AspectJ -> cflow / cflowbelow) arbeiten.

Gruß Tom
 
Zurück