2009-05-31 11 views
10

He utilizado el siguiente código en una serie de aplicaciones para cargar ensamblajes .DLL que exponen plugins..NET Assembly Plugin Security

Sin embargo, antes siempre me preocupaba la funcionalidad, en lugar de la seguridad.

Ahora estoy planeando utilizar este método en una aplicación web que podría ser utilizada por otros grupos además de mí, y me gustaría asegurarme de que la seguridad de la función sea rápida.

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. 
} 

Las pocas primeras preocupaciones que tienen:

  1. Esta función lee todo el directorio y no se preocupa por lo que las asambleas se carga, y en su lugar sólo se carga a todos ellos. ¿Hay alguna forma de detectar si un ensamblado es un ensamblado .NET funcional y válido antes de cargarlo con Assembly.LoadFile()?
  2. ¿Qué tipo de manejo de excepciones se debe agregar a la función para evitar que la inicialización del ensamblaje interrumpa mi código?
  3. Si deseo negarle a la asamblea el derecho de hacer lo siguiente: Leer/Escribir archivos, Leer/Wite el registro, etc., ¿cómo lo haría?

¿Hay alguna otra preocupación de seguridad que me preocupe?

EDITAR: Tenga en cuenta que quiero que alguien pueda escribir un complemento, pero igual quiero estar seguro.

Respuesta

12

1) nombre fuerte el conjunto con una cierta clave.

  • hacer no tienen que ponerlo en la GAC ​​
  • puede volver a utilizar una clave para firmar más de un ensamblaje
  • cuando se vuelva a utilizar la tecla, se obtiene el mismo " clave pública" de cada conjunto firmado

2) en la carga, comprobar que el montaje ha sido fuerte llamada con la tecla que está esperando

  • Puede almacenar la clave pública como un archivo binario, un recurso incrustado, o usar la clave pública existente del conjunto de la ejecución de
  • esta última forma puede no ser la mejor manera que es posible que desee diferenciar asambleas firmado con la tecla "plugin" de los firmados con una clave habitual)

Ejemplo:

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; 

nota: código no ha sido probado o compilado, puede contener errores de sintaxis.

+0

La documentación de MSDN me confundió un poco con respecto a su respuesta. Por favor vea mi pregunta aquí http://stackoverflow.com/q/32016340/1462656. – erotavlas

+0

Es un comentario interesante. Por lo que entiendo, un ensamblaje firmado indica 1) se ha firmado con una determinada clave y 2) no se pudo haber modificado después de la firma. Por lo tanto, técnicamente correcto, puede usar esto para verificar la identidad, pero no necesariamente la seguridad, o más bien la diferencia entre Authetication (el complemento está firmado y no alterado) frente a Authorization (el código está autorizado para realizar una determinada acción). –

2
  1. Si está preocupado por la seguridad de sus asambleas, debe Strongly Name ellos. Esto proporciona un alto nivel de seguridad de que el conjunto es de hecho el que usted pretende que sea.

  2. Las excepciones que puede encontrar durante la carga son las siguientes. Añadir try/catch alrededor de su intento de carga en Assembly.Load() y reaccionar de acuerdo con el tipo de error:

    • ArgumentNullException
    • FileLoadException
    • FileNotFoundException
    • BadImageFormatException
  3. Asambleas le cargar dinámicamente debe tener los mismos derechos que la cuenta de usuario que los cargó, a menos que este ensamble esté en el GAC. Cree una cuenta de servicio con los derechos que desea y ejecute su aplicación usando esta cuenta para controlar el acceso.

+0

me gusta su respuesta a # 2, pero para # 3, ¿puedo "encarcelar" el ensamblaje que estoy cargando para que mi aplicación pueda ejecutarse gratis, pero el complemento es limitado? –

+0

Por cierto, estoy bastante seguro de que IIS + ASP.NET puede "encarcelar" las aplicaciones. –

3

No sé si esta es la mejor manera, pero cuando se llama LoadFile en una asamblea inválida obtendrá un BadImageFormatException porque la Asamblea no tiene un manifiesto.

La forma en que se escribe su código actualmente está bastante abierto a un Process Control Attack. Cualquiera que pueda obtener acceso al directorio y desplegar un ensamblado que implemente su interfaz puede ejecutar este ataque. Ni siquiera tienen que implementar la interfaz muy bien, solo pueden proporcionar un constructor predeterminado y hacer todo el daño en eso. Esto le permite a un atacante ejecutar código bajo el privilegio de su aplicación, que siempre es malo.

Por lo tanto, su única protección actual es proteger el acceso al directorio en un nivel de sistema operativo. Esto puede funcionar para usted, pero es solo un nivel de defensa y depende del estado de seguridad que no puede controlar.

Aquí hay dos cosas que podría tener en cuenta:

  1. fuerte nombrar el montaje y que requiere el montaje estar registrado en la GAC ​​es muy probable que la forma más segura de hacer esto. Si puede hacer esto, necesita encontrar una forma de proporcionar el nombre completo de la Asamblea a su aplicación y cargarlo con Assembly.Load().

Sin embargo dudo que desea instalar estos plugins en la GAC ​​por lo que podría hacerlo de esta manera:

  1. dentro de su aplicación proporciona una manera para que los usuarios registrar una complemento, esencialmente un mini-GAC. Cuando lo hacen, almacena la ubicación y el nombre del ensamblado, así como la clave pública. Esto requiere que la Asamblea tenga un nombre fuerte.

De esta forma solo cargará los ensambles que haya proporcionado alguien con privilegios a su aplicación, muy probablemente alguien que tenga derecho a agregar un complemento. Antes de cargar el ensamblaje, puede verificar que la clave pública coincida con la que se proporcionó cuando se registró el ensamblaje para evitar que un atacante simplemente reemplace el ensamblado. Este código es bastante simple:

private bool DoPublicKeysCompare(Assembly assembly, byte[] expectedPublicKey) 
    { 
     byte[] assemblyKey = assembly.GetName().GetPublicKey(); 
     return expectedPublicKey.SequenceEqual(assemblyKey); 
    } 

Así que ahora para ejecutar un ataque contra alguna manera debo obtener el privilegio de cambiar el valor PublicToken y obtener acceso al directorio y cambiar el archivo.

1

Eche un vistazo al framework AddIn de Microsoft, ya que proporciona algunas de las funciones que está buscando, como la seguridad y el aislamiento.

También recomendaría que utilice métodos solo de reflexión como ReflectionOnlyLoad y ReflectionOnlyLoadFrom para garantizar que no se ejecute nada del ensamblaje desde el que está consultando los metadatos mientras lo examina. Una vez que determine que el ensamblaje tiene lo que está buscando solo entonces puede cargarlo.

Cuestiones relacionadas