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, rather than security.
I am now planning to use this method on a web application that could be used by groups other than me, and I would like to make sure that the security of the function is up-to-snuff.
private void LoadPlugins(string pluginsDirectory)
{
List<IPluginFactory> factories = new List<IPluginFactory>();
foreach (string path in Directory.GetFiles(pluginsDirectory, "*.dll"))
{
Assembly assembly = Assembly.LoadFile(path);
foreach (Type type in assembly.GetTypes())
{
IPluginEnumerator instance = null;
if (type.GetInterface("IPluginEnumerator") != null)
instance = (IPluginEnumerator)Activator.CreateInstance(type);
if (instance != null)
{
factories.AddRange(instance.EnumerateFactories());
}
}
}
// Here, I would usually collate the plugins into List<ISpecificPlugin>, etc.
}
The first few concerns I have:
Are there any other security concerns I should be worried about?
EDIT: Keep in mind that I want anybody to be able to write a plug-in, but I still want to be secure.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With