tags:

views:

185

answers:

7

interfaces provide a useful abstraction capability. One can have a class Foo implement some interfaces, say A, B, and C. Some client code may get a reference of type A, others one of type B, etc. each actually the same Foo object but the interface exposing only a narrow subset of the functionality. Of course, evil client code can try to cast the A reference to Foo, then access the other functionality.How to prevent this?

+1  A: 

You can't. One workaround is to implement three proxy classes, one to implement each interface, that forward all calls to a single Foo instance.

Marcelo Cantos
"you can make Foo a private or internal class". The client could still cast to B or C (the interfaces are all public).
Thilo
In addition, `Foo` may be needed by other classes in the main project so which would mean it must be public.
Kevin Brock
Thanks for the comments. I should have stuck to my original advice.
Marcelo Cantos
+9  A: 

This is called a "malicious cast" and you can prevent it by having a wrapper that implements only the narrow interface you want to expose (by delegating to a private reference to the object that you would have otherwise directly passed to the evil client).

However, if the client is not only evil, but powerful as well, he might be able to use reflection to get to the hidden reference anyway.

Thilo
Add to this a `SecurityManager` and you can reduce and/or eliminate the possibility of using reflection.
Kevin Brock
+2  A: 

Normal inheritance will always allow it, you can do nothing with it. If you want to expose some class as interface but hide other methods use Adapter pattern (google it)

Andrey
A: 

Hide the underlying object.

Let's say you have:

public interface A {
}

public class B implements A {
}

So, interface A implements just a subset of B's functionality. Effectively it hides parts of B. Your question is how to stop the user from downcasting A to a B.

B objectOfTypeB = (B)objectOfTypeA; // you don't want this

So, don't give the user access to class B. If the user can't import it, he can't instantiate it or downcast to it. So, he's force to use the interface and nothing more.

Change the above code to:

/* Publicly accessable interface */
public interface A {
}

/* Class hidden inside the package. */
public class B implements A {
}

Then, you can just have a function return an A, secure in the knowledge that the user can't use B.

/* Function that returns an A. */
public A foo() {
    /* ... */
    return objectOfTypeB;
}
scvalex
The client could still cast to B or C which he is not supposed to access (the interfaces are all public).
Thilo
If the client doesn't have access to B, he can't cast to B either. e.g. in Java you could have a factory returning B where B is package protected.
nos
Why make things complicated? You have a function that returns an A. Since the user doesn't have access to class B, he can't use anything from it. There's no need for factory methods; scoping is enough to solve this problem.
scvalex
How can you hide a `public` class inside a package? If the client class is "malicious" the author would just examine the contents of the jar (package) and find all the available public classes that they could experiment with.
Kevin Brock
@scvalex, if the user doesn't have access to B, it means he must call something that creates B for him - which in this case I consider a "factory method", otherwise he could just do new com.foo.B(), or cast the result to com.foo.B
nos
A: 

You can use a Facade class.

This class should wrap a delegate of class Foo and then only expose interface methods of, say A and just forward them to the delegate.

On the other hand, you can prevent casting to Foo by declaring it package private and have a public factory method that returns just the interface A ( which in reality is Foo ). That way casting from other packages will not be possible ( still, somebody may play tricks with reflection ).

Alexander Pogrebnyak
Using just package protected doesn't always work as the OP may have other packages in his project and these need to have access to this class.
Kevin Brock
A: 

There is no really practical, non-invasive way to protect against this.

However, if your situation really requires this protection, use this utility class to create dynamic proxy (delegate) classes (adapted from Dynamic Proxy Classes - <50 lines of production code!!).

This will cause ClassCastExceptions at runtime if someone uses tries a malicious cast. You could even conditionalize the code to turn it off at production time (have newInstance() just return obj - the object to as the "proxy").

DynamicProxy.java

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DynamicProxy implements java.lang.reflect.InvocationHandler {

    private Object obj;

    public static Object newInstance(Object obj, Class<?>... interfaces) {
        if (interfaces == null || interfaces.length == 0) {
            throw new IllegalArgumentException("No interfaces");
        }
        return java.lang.reflect.Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            interfaces,
            new DynamicProxy(obj));
    }

    private DynamicProxy(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method m, Object[] args)
    throws Throwable
    {
        Object result;
        try {
            result = m.invoke(obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " +
                           e.getMessage());
        }
        return result;
    }

    // ** DEMO CODE BELOW HERE **

    interface A {
        void methodA();
    }

    interface B {
        void methodB();
    }

    static class Foo implements A, B {
        public void methodA() { System.out.println("A"); }
        public void methodB() { System.out.println("B"); }
    }

    public static void main(String[] args) {

        Foo foo = new Foo();  // implements both interfaces

        // calls foo's methods, but only A methods
        A a = (A) DynamicProxy.newInstance(foo, A.class);

        // calls foo's methods, but only B methods
        B b = (B) DynamicProxy.newInstance(foo, B.class);

        // calls foo's methods, but only B methods
        A ab = (A) DynamicProxy.newInstance(foo, A.class, B.class);

        a.methodA();
        b.methodB();
        ab.methodA();
        ((B) ab).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$Foo
        ((Foo) a).methodA();

        // ClassCastException: $Proxy1 cannot be cast to DynamicProxy$Foo
        ((Foo) b).methodB();

        // ClassCastException: $Proxy0 cannot be cast to DynamicProxy$B
        ((B) a).methodB();

        // ClassCastException: $DynamicProxy1 cannot be cast to DynamicProxy$A
        ((A) b).methodA();
    }
}
Bert F
+1  A: 

The person who performs a malicious cast does so at their own risk. In almost all cases, you can safely assume that the user will not use an object in a manner outside the specified interface contract.

The only time you really need to use a proxy object is if you are exposing security-sensitive object to untrusted code. Otherwise, spend your time making clear documentation about how objects can be used and work under the assumption that it will be followed.

280Z28