I have used the following code in a number of applications to load .DLL assemblies that expose plugins.
However, I previously was always concerned with functionality
If you are concerned with the security of your assemblies, you should Strongly Name them. This provides a high level of security that the assembly is indeed the one you intend for it to be.
Exceptions you might encounter during load are as follows. Add try/catch around your load attempt at Assembly.Load() and react according to the error type:
Assemblies you load dynamically should have the same rights as the user account which loaded them unless this assembly is in the GAC. Create a service account with the rights you desire, and run your application using this account to control access.
1) strong name the assembly with a certain key.
2) on load, check that the assembly has been strong named with the key you're expecting
Example:
public static StrongName GetStrongName(Assembly assembly)
{
if(assembly == null)
throw new ArgumentNullException("assembly");
AssemblyName assemblyName = assembly.GetName();
// get the public key blob
byte[] publicKey = assemblyName.GetPublicKey();
if(publicKey == null || publicKey.Length == 0)
throw new InvalidOperationException( String.Format("{0} is not strongly named", assembly));
StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
// create the StrongName
return new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
}
// load the assembly:
Assembly asm = Assembly.LoadFile(path);
StrongName sn = GetStrongName(asm);
// at this point
// A: assembly is loaded
// B: assembly is signed
// C: we're reasonably certain the assembly has not been tampered with
// (the mechanism for this check, and it's weaknesses, are documented elsewhere)
// all that remains is to compare the assembly's public key with
// a copy you've stored for this purpose, let's use the executing assembly's strong name
StrongName mySn = GetStrongName(Assembly.GetExecutingAssembly());
// if the sn does not match, put this loaded assembly in jail
if (mySn.PublicKey!=sn.PublicKey)
return false;
note: code has not been tested or compiled, may contain syntax errors.
Take a look at AddIn framework from Microsoft as it provides some of the capabilities that you're looking for such as security and isolation.
I would also recommend that you use reflection only methods such as ReflectionOnlyLoad and ReflectionOnlyLoadFrom to ensure nothing from the assembly you're querying metadata from gets executed as you examine it. Once you determine that the assembly has what you're looking for only then you can load it.
My own preference is to use Assembly.ReflectionOnlyLoadFrom(...)
see the following MSDN article for more information:
How to: Load Assemblies into the Reflection-Only Context
If you're running 3.5, there's a lot to be said for the new System.AddIns stuff - take a look over at http://www.codeplex.com/clraddins for examples.
-Oisin
I don't know if this is the best way but when you call LoadFile on an invalid assembly you will get a BadImageFOrmatException because the assembly does not have a manifest.
The way your code is written currently you are pretty wide open to a Process Control Attack. Anyone who can get access to the directory and drop down an assembly that implements your interface can execute this attack. They do not even have to implement the interface very well, they can just provide a default constructor and do all the damage in that. This allows an attacker to execute code under the privilege of your application which is always bad.
So your only current protection is to protect access to the directory on an OS level. This may work for you but it is only one level of defense and you are reliant on security state you cannot control.
Here are two things you could look at:
However I doubt you want to install these plugins into the GAC so you could do it this way:
This way you will only load Assemblies that someone with privilege to your application has provided, most likely someone who has rights to add a plugin. Before you load the Assembly you can check that the Public Key matches what was provided when the Assembly was registered to prevent an attacker from just replacing the Assembly. This code is fairly simple:
private bool DoPublicKeysCompare(Assembly assembly, byte[] expectedPublicKey)
{
byte[] assemblyKey = assembly.GetName().GetPublicKey();
return expectedPublicKey.SequenceEqual(assemblyKey);
}
So now to execute an attack on you I must somehow get privileged to change the PublicToken value and get access to the directory and change out the file.