Securing the MEF Directory Catalog
Securing the MEF Directory Catalog
during my MEF lecture, on of the attendant (a fellow from check-point) ask the following question:
"How can we secure our catalog?"
this is a really good point, because without securing our catalog,
hackers can exploit our extensibility model for injecting malicious code.
so can we defend our MEF gates?
the short answer is yes.
and the long one is yes but…
Secure techniques
I will mention 3 techniques, each defend our code on different layer:
- IT layer
- CAS (code access security) layer
- Catalog layer (which involving some changes to the Directory Catalog)
(I'm currently speaking with the MEF team trying to convince them to add the 3rd technique into the MEF core).
you may use all the 3 techniques together, for getting a multi layer protection.
IT level protection
the IT layer protection is very simple and straight forward,
all you have to do is to restrict the access privilege to the plug-ins directory,
in order to prevent none authorized plug-ins deployment.
CAS (code access security) protection
because MEF is contract based (means that MEF will only discover parts that match specific contract),
we can defend our contract by using CAS technique like demanding specific strong name (or any other evidence).
the secured contract may look like:
Code Snippet
- [StrongNameIdentityPermission(SecurityAction.Demand, PublicKey = "00240000048...")]
- public interface ILogger
- {
- void Write(string message);
- }
as we can see in line 1, by restricting the access to our contract we can prevent
none authorized code from implementing it.
Adding predicate to the MEF Directory Catalog
Note: the secured version of the Directory Catalog is not currently in the box,
but i hope it will get in before the release time.
if you want to use it now you can download the modified MEF bits from my sky drive here.
thinking about the security risk I came to conclusion that the
mitigation should handle just before the assembly loading.
first it is more efficient and second we cannot handle the security after the instantiation
because it will be to late (constructor code can be malicious too).
Directory Catalog API modifications
the only change needed in terms of the public API is to add new constructor
to the Directory Catalog, which will get lambda expressions (predicate)
that will authorize the assembly loading (just before the actual loading).
the new constructor definition is:
Code Snippet
- public DirectoryCatalog(string path, string searchPattern, Predicate<AssemblyName> isAuthorized)
all you have to do is give a predicate with AssemblyName parameter so you restrict the
assembly loading upon the AssemblyName information.
using it may look like:
Code Snippet
- var catalog = new DirectoryCatalog(path, "*.*",
- info => info.KeyPair.PublicKey == ...);
Summary
the MEF release is getting close, but I'm hoping that
this minor yet very important API change will get into the release.
so we will have an elegant way to secure our extension points out of the box.
Point of Interest
the code for the modified MEF code can be download from here.
if you interesting is which modification was needed at the MEF core level
in order to have this new API, I will describe it in this section.
actually as you will shortly see, I was changing only a few lines of code.
the reason that I decided to change the core rather of creating new catalog type was:
1. it seem like a feature that should ship as part of the MEF core.
2. because some of the changes was needed on internal methods,
building new catalog would result with coping half of the MEF core.
Changes made step by step:
Step 1:
Code Snippet
- private Predicate<AssemblyName> _isAuthorized = (name) => true;
-
- public DirectoryCatalog(string path, string searchPattern, Predicate<AssemblyName> isAuthorized)
- {
- Requires.NotNullOrEmpty(path, "path");
- if (isAuthorized != null)
- _isAuthorized = isAuthorized;
-
- this.Initialize(path, searchPattern);
- }
the above is the code of the new DirectoryCatalog constructor that include the authorization predicate.
as you can see in lines 6,7 I'm storing the predicate in private member (line 1)
Step 2:
the DirectoryCatalog class is having private method that responsible for creating AssemblyCatalog.
Code Snippet
- private AssemblyCatalog CreateAssemblyCatalogGuarded(string assemblyFilePath)
- {
- Exception exception = null;
-
- try
- {
- return new AssemblyCatalog(assemblyFilePath, this, _isAuthorized);
- }
- catch (FileNotFoundException ex) { // Files should always exists but don't blow up here if they don't
- exception = ex;
- }
- catch (FileLoadException ex) { // File was found but could not be loaded
- exception = ex;
- }
- catch (BadImageFormatException ex) { // Dlls that contain native code are not loaded, but do not invalidate the Directory
- exception = ex;
- }
- catch (ReflectionTypeLoadException ex) { // Dlls that have missing Managed dependencies are not loaded, but do not invalidate the Directory
- exception = ex;
- }
- catch (Security.SecurityException ex) {
- exception = ex;
- }
- CompositionTrace.AssemblyLoadFailed(this, assemblyFilePath, exception);
- return null;
- }
I was making 2 changes in this class:
1. at line 7, I added the authentication predicate to the AssemblyCatalog creation.
2. at lines 21-23, I added catch for security exception (so none authorized assemblies will be traced while the application will not crash)
Step 3:
adding internal constructor to the AssemblyCatalog
Code Snippet
- internal AssemblyCatalog(string codeBase, ICompositionElement definitionOrigin,
- Predicate<AssemblyName> isAuthorized)
- : this(LoadAssembly(codeBase, isAuthorized), definitionOrigin)
- {
- }
at line 3 you can see that I forward the authentication predicate to the LoadAssembly method.
Step 4:
the last modification was at the assembly loading level
Code Snippet
- private static Assembly LoadAssembly (string codeBase, Predicate<AssemblyName> isAuthorized)
- {
- Requires.NotNullOrEmpty(codeBase, "codeBase");
-
- AssemblyName assemblyName;
-
- try
- {
- assemblyName = AssemblyName.GetAssemblyName(codeBase);
- }
- catch (ArgumentException)
- {
- assemblyName = new AssemblyName();
- assemblyName.CodeBase = codeBase;
- }
-
- if (!isAuthorized(assemblyName))
- throw new SecurityException(assemblyName.Name + " is not authorized");
- return Assembly.Load(assemblyName);
- }
1. at the method signature I added the authorization predicate
2. at lines 17,18 we use the authentication predicate to determine whether the assembly
is authorized (this happens before the actual loading of the assembly)
Conclusion
with a few minor steps we got match secured catalog,
once again i hope that i will manage to get into the release bits.