Exposing objects and permissioning

rbulph

Junior Contributor
Joined
Feb 17, 2003
Messages
397
If you expose an object to a plug-in you are opening yourself up to the possibility that the plug-in might change properties of the object. This is because the object will be passed ByRef even if the procedure that the plug-in implements declares the object parameter as ByVal (try it if you don't believe me). So is there a way to effectively make all the properties of an object read-only to certain dlls? I suppose you could do this by having a flag to be set when the object is passed out to a plug-in, and checking for the state of this flag in every Property Set in your object. But it's a bit tedious. Is there a better way?
 
As a first step, I would declare certain members with the internal keyword in C# and the Friend keyword in VB, which declares something as visible only to the declaring assembly (i.e. the plug-in can't access them, but neither can any other external libraries if they need to). In C# 2 (and maybe VB8, I'm not sure) you can declare different property accessors with different visibility, like so.
C#:
public int SomeIntProperty{
    get{
        return someBackingField;
    }
    internal set{
        someBackingField = value;
    }
}

Secondly, this is a matter of design. The objects exposed to the plug-in should only generally expose that which the object needs to manipulate. If the plug-in is given objects which in can manipulate in a malicious way you may want to re-examine your design and object model. For instance, let's look at an application that supports plug-ins which may need to be able to manipulate a form.

I would hesitate to pass the form directly to the plug-in, because the plug-in now has full control over the form and all the controls that it contains. One solution would be to have the form implement an interface that would be shared with the plug-in that only defines methods necessary for the plug-in, and pass any forms to the plug-in as an instance of that interface. This has the drawback that the objects can still be cast back to a Form, but it does explicity let plug-in coders know what they should and shouldn't do.

Another solution would be to create a class to act as a proxy for the form, and only provide those methods that are needed for the plug-in. Since the plug-in can't get direct access to the form, it can only do what you specifically allow it to do.

Of course, if the plug-in is running with full trust, no matter what you do someone can use reflection to find its way back to an important Form or something else that it can manipulate and cause problems, so you might want to check whether or not there is a way to load DLLs with partial trust.
 
Thanks. Yes, you can have properties with different accessors for the Set and Get in VB8. I wasn't aware of that. But (why does there always have to be a but?) the property grid seems to act as if it is external to the assembly which it's displayed in, i.e. it shows properties declared as you suggest disabled. And I do need to use the property grid. This is the code I have:

Visual Basic:
Public Class Form1

    Dim f As New Class1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        f.Prop = 6   'no problem 
        Me.PropertyGrid1.SelectedObject = f '  "Prop" is disabled in the grid!

    End Sub

End Class

Public Class Class1
    Private pd As Long = 5
    Public Property Prop() As Long
        Get
            Return pd
        End Get
        Friend Set(ByVal value As Long)
            pd = value
        End Set
    End Property
End Class

Hmm.
 
A dirty hack would be to flag the class that you are exposing to the plugin with the [Serializable] attribute. Then, before you call the plugin method, you would calculate the hash of the class using MD5 or something of the like. You would then call the plugin method (passing the object) and, after it completes, calculate the hash for the object again. If the two hashes are different then you know something changed. You could then throw an exception.

This doesn't completely solve the problem but might provide some help.
 
Gill Bates said:
A dirty hack would be to flag the class that you are exposing to the plugin with the [Serializable] attribute. Then, before you call the plugin method, you would calculate the hash of the class using MD5 or something of the like. You would then call the plugin method (passing the object) and, after it completes, calculate the hash for the object again. If the two hashes are different then you know something changed. You could then throw an exception.

This doesn't completely solve the problem but might provide some help.
An idea, certainly. Another idea I had was based on the fact that all of the objects I expose have custom type converters attributes and each property has a custom property descriptor. I thought perhaps I could override the SetValue method of the custom property descriptor class to only set the value when a certain flag was set to indicate that the code was running within the application. But it seems that the SetValue method is only called when the property is changed through a property grid, so that was no good.

I think the best thing may be to just have a flag that I check in every Property Set method. It's not actually that much trouble.
 
Gill Bates said:
A dirty hack would be to flag the class that you are exposing to the plugin with the [Serializable] attribute. Then, before you call the plugin method, you would calculate the hash of the class using MD5 or something of the like. You would then call the plugin method (passing the object) and, after it completes, calculate the hash for the object again. If the two hashes are different then you know something changed. You could then throw an exception.

This doesn't completely solve the problem but might provide some help.
I'm not sure this would work, would it? I don't know what MD5 is, and maybe what I'm doing below is not the same as your suggestion, but it gives me the same value both before and after changing the property of the object.

Visual Basic:
Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim M As New abc
        Debug.Print(M.GetHashCode)  'returns 39086322
        M.name = "H"
        Debug.Print(M.GetHashCode)  'returns 39086322
    End Sub
End Class

<Serializable()> Public Class abc
    Public name As String
End Class
 
If you wanted to use the hashcode like that you'd need to override GetHashcode() and return a value derived from the content of the class rather than a strict identity value as it is now. It is inadvisible to alter the hashcode of something during it's lifetime, the same object should always return the same hashcode. Even if you decided to use this method i wanted to attack your application i'd just derive from your type and override hashcode to return the initial value which i'd save as a new field.

Using Protected Private and Internal accessibility declarations as well as possible wrapper and immutable classes is probably your best approach.
 
I agree that the best approach is to do what Wraith suggested. Regardless, here's a simple example of what I was talking about:
C#:
using System;
using System.Security.Cryptography;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Example
{
    [Serializable]
    class MyClass
    {
        private int _i;

        public MyClass(int i)
        {
            _i = i;
        }

        public int I
        {
            get { return _i; }
            set { _i = value; }
        }
    }

    class Program
    {
        static byte[] Serialize(MyClass m)
        {            
            BinaryFormatter formatter = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            formatter.Serialize(ms, m);
            return ms.ToArray();
        }

        static string CalculateMD5(MyClass m)
        {
            byte[] serialized = Serialize(m);
            byte[] hashed = SHA1.Create().ComputeHash(serialized);
            return BitConverter.ToString(hashed).Replace("-", "");
        }

        static void PluginMethod(MyClass m)
        {
            m.I = 300;
        }

        static void Main(string[] args)
        {
            MyClass m = new MyClass(100);

            string hashBefore = CalculateMD5(m);

            PluginMethod(m);

            if (hashBefore != CalculateMD5(m))
            {
                Console.Write("Something changed...");
            }

            Console.ReadLine();
        }
    }
}
 
Wraith said:
If you wanted to use the hashcode like that you'd need to override GetHashcode() and return a value derived from the content of the class rather than a strict identity value as it is now. It is inadvisible to alter the hashcode of something during it's lifetime, the same object should always return the same hashcode. Even if you decided to use this method i wanted to attack your application i'd just derive from your type and override hashcode to return the initial value which i'd save as a new field.

Using Protected Private and Internal accessibility declarations as well as possible wrapper and immutable classes is probably your best approach.

I think Internal is a JScript keyword - certainly it doesn't seem to be recognised in VB. I'm not sure what a wrapper class is or how it would help. As for immutable classes, there's an ImmutableObjectAttribute that you can apply in VB to a class, but I think it stops the object's properties from showing in a property grid, so that would be no good for me. So as far as I can see, my route of having a flag to record whether an external procedure has been called, and checking the value of that in all Property Sets is still the best way.
 
MD5 is a hash algorithm used to produce a 128-bit digest. It is completely unrelated to the GetHashCode method.
 
As a related matter, I notice that .net allows a dll to reference an exe. In VB6 this was not allowed, you had to remove everything that you wanted to expose from the exe into a dll, to be referenced by both the exe and any additional dlls. So, if you were catering for plug-ins, you would have an exe, a dll with interfaces and your object model (the "class library"), and any number of dll plug-ins. As a matter of project design, would it now be reasonable for me to do away with the class library, put everything currently in there into the exe and have the plug-ins reference the exe rather than any class-library? It would do away with quite a lot of events that the class library has to raise. But perhaps a plug-in writer expects to refer to a class-library rather than an exe?
 
I don't understand why your class library needs to raise events in the first place. For a typical plugin, you define an interface and any plugin can then implement that interface. No events are really needed.
 
Gill Bates said:
I don't understand why your class library needs to raise events in the first place. For a typical plugin, you define an interface and any plugin can then implement that interface. No events are really needed.
The class library needs to do this to communicate with the main application. It can't have a reference to the main application because that would be circular, so I have to either have WithEvents references to class library objects in the main application and raise events from those objects, or have an object in the main application implement an interface defined in the class library for the purposes of sending messages to the application. Both of those could be avoided if I just merge the class library and the application.
 
Why not just have the main application check the current plugin assembly for classes that implement the plugin interface (which is located in the class library)? If the plugin class implements the interface then you create an instance of it, cast it to the interface, and then call whatever methods/properties you need.
 
A common method to implement plug-ins is to have a main application EXE, a separate DLL that defines the plug-in interface (and possibly a host interface to allow the plug-in to invoke methods on the host environment), and both the EXE and plug-ins should reference the DLL, which references neither. This eliminates circular dependencies and provides an API for programmers to write plug-ins without a dependency on the actual EXE.

This is not necessary, though. It is acceptable for the main EXE to define the plug-in interface, and the host interface if there is one. The plug-in can reference the EXE to obtain the API (i.e. interfaces). This is the important part, which Gill Bates pointed out: The main EXE does not need to, and further more, should not, reference the plug-in DLL. Rather, the main EXE should use reflection to load the plug-in DLL and search for classes that implement the plug-in interface. You can think of this as a sort of late-bound dependency to circumvent circular dependency issues, although it introduces a great deal more flexibility than just that.

I mentioned a "host interface" twice, so I will explain exactly what I mean by that now. A plug-in system usually defines an interface for the plug-in. Something like IPlugIn. You can also declare a host interface that the hosting application should provide to the plug-in, for example, IPlugInHost. IPlugIn should expose a property that accepts an IPlugInHost and when the host application instantiates the plug-in class it should assign an instance of an IPlugInHost to the said property. This provides the plug-in with a method to make callbacks to the host, eliminating the need for the use of events. But I highly recommend that you carefully consider whether or not you need either a host interface or events. Generally all the feedback you need to get from a plug-in can be obtained by return values from function calls into the plug-in.

Just some notes on other posts:
  • internal in C# is the same as Friend in VB.
  • As was said, VB6 projects could not reference EXEs, only DLLs, and the same applies to .Net 1.x, but in .Net 2.0 EXEs and DLLs can both reference EXEs and DLLs. This makes things easier in some situations. For example, if you provided a feature-rich file compression application and wanted its functions to be available through its normal GUI but also available in code for other programmers you no longer need to put the good stuff in a DLL and distribute two files. In .Net 2.0 you can distribute it all as a single file, declaring what you want to be accessible to other coders as public.
 
Hi, This post is very informative, however I would like some specific information. If someone can help me then please send me a private message. Best Regards,
 
marble_eater said:
A common method to implement plug-ins is to have a main application EXE, a separate DLL that defines the plug-in interface (and possibly a host interface to allow the plug-in to invoke methods on the host environment), and both the EXE and plug-ins should reference the DLL, which references neither. This eliminates circular dependencies and provides an API for programmers to write plug-ins without a dependency on the actual EXE.

This is not necessary, though. It is acceptable for the main EXE to define the plug-in interface, and the host interface if there is one. The plug-in can reference the EXE to obtain the API (i.e. interfaces). This is the important part, which Gill Bates pointed out: The main EXE does not need to, and further more, should not, reference the plug-in DLL. Rather, the main EXE should use reflection to load the plug-in DLL and search for classes that implement the plug-in interface. You can think of this as a sort of late-bound dependency to circumvent circular dependency issues, although it introduces a great deal more flexibility than just that.

I mentioned a "host interface" twice, so I will explain exactly what I mean by that now. A plug-in system usually defines an interface for the plug-in. Something like IPlugIn. You can also declare a host interface that the hosting application should provide to the plug-in, for example, IPlugInHost. IPlugIn should expose a property that accepts an IPlugInHost and when the host application instantiates the plug-in class it should assign an instance of an IPlugInHost to the said property. This provides the plug-in with a method to make callbacks to the host, eliminating the need for the use of events. But I highly recommend that you carefully consider whether or not you need either a host interface or events. Generally all the feedback you need to get from a plug-in can be obtained by return values from function calls into the plug-in.

Just some notes on other posts:
  • internal in C# is the same as Friend in VB.
  • As was said, VB6 projects could not reference EXEs, only DLLs, and the same applies to .Net 1.x, but in .Net 2.0 EXEs and DLLs can both reference EXEs and DLLs. This makes things easier in some situations. For example, if you provided a feature-rich file compression application and wanted its functions to be available through its normal GUI but also available in code for other programmers you no longer need to put the good stuff in a DLL and distribute two files. In .Net 2.0 you can distribute it all as a single file, declaring what you want to be accessible to other coders as public.
Thanks for the overview. As regards the host interface which you refer to, I prefer to use this than have functions which return all the information. This is because the methods which I require plug-ins to implement can return one or more objects. It's easier for the plug-in writer to have a reference to an object in the application which implements the host interface and be able to go "DataHandler.Add X" with this for each object than for him to have to worry about building up a collection/array of these objects and then return that at the end of the function. I suppose I could also send a ByRef Collection object to each plug-in interface method to be filled, but that would rather lengthen the method declarations.

As I say, I also have events in the DLL which inform the exe when properties of certain objects have changed, because the display shown in the exe reflects some of those properties and needs to be updated when they have changed. I think that to avoid that I may now go and merge my class library into my exe. One thing that occurs to me in doing this is that I need to make sure that all forms and user controls in the exe are Friend classes - I certainly don't want the plug-ins being able to access them. When I add a new form or user control to a project it is Public by default so I will have to watch it - I don't know if there is a way to change this?
 
I've done this and it's made things a lot simpler. There were even more "hoops" that I'd had to jump through to communicate between the class library and the exe than I had remembered, and now I have none of that.

I also note that in the Project's property page you can declare a Root Namespace, so can give your project a namespace like "MyClassLibrary", so that the plugins import that rather than the name of your project, which seems more intuitive, and removes any concerns I might have had about a plug-in writer expecting to access a class library.
 
Back
Top