Why You Should Sign Your DLLs

8/7/2011

Generally speaking I do not deal with security issues much. I mean I can set up SSL, set up a form based authentication, etc. the basic stuff. But one thing that I almost always do in my applications is sign the DLLs. The main reason I do this is because of a former coworker of mine.

You see, years ago I worked with a guy who liked to mess with people. He liked to put new hires through a "hazing" ritual. Specifically he had this application that he wrote that would analyze a DLL, find the class/functions that were available in the DLL, and generate some default classes with the same names (basically he generated stubs). He would do this with one of the third party DLLs that we were using, slip it onto your machine, and come over and tell you that whatever app you had just shipped wasn't working correctly. You'd start up the app and nothing would work. It would start up fine, but everything from the one DLL would return null/default values.

The reason he was able to do this was because the way that DLLs are loaded is dumb. I don't mean it's done in a stupid way, well it is sort of. When you link to a DLL that is named MyDLLs.dll, it will load anything as long as it has the same name by default. As an example, let's assume that I have an app called TestApp:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using Utilities.DataTypes.Patterns;
   6:  
   7: namespace TestApp
   8: {
   9:     class Program
  10:     {
  11:         static void Main(string[] args)
  12:         {
  13:             Factory<string, string> Factory = new Factory<string, string>();
  14:             Factory.Register("A", "B");
  15:             Console.WriteLine(Factory.Create("A"));
  16:             Console.ReadLine();
  17:         }
  18:     }
  19: }

It's simple enough, all it's doing is using the default System DLLs and one extra DLL called Utilities (and yes, it's a class from my utility library but let's assume that it's not). The Utilities DLL has a class called Factory:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace Utilities.DataTypes.Patterns
   7: {
   8:     public class Factory<Key, T>
   9:     {
  10:         #region Constructors
  11:  
  12:         /// <summary>
  13:         /// Constructor
  14:         /// </summary>
  15:         public Factory()
  16:         {
  17:             Constructors = new Dictionary<Key, Func<T>>();
  18:         }
  19:  
  20:         #endregion
  21:  
  22:         #region Protected Variables
  23:  
  24:         /// <summary>
  25:         /// List of constructors/initializers
  26:         /// </summary>
  27:         protected virtual Dictionary<Key, Func<T>> Constructors { get; set; }
  28:  
  29:         #endregion
  30:  
  31:         #region Public Functions
  32:  
  33:         /// <summary>
  34:         /// Registers an item
  35:         /// </summary>
  36:         /// <param name="Key">Item to register</param>
  37:         /// <param name="Result">The object to be returned</param>
  38:         public virtual void Register(Key Key, T Result)
  39:         {
  40:             if (Exists(Key))
  41:                 Constructors[Key] = new Func<T>(() => Result);
  42:             else
  43:                 Constructors.Add(Key, new Func<T>(() => Result));
  44:         }
  45:  
  46:         /// <summary>
  47:         /// Registers an item
  48:         /// </summary>
  49:         /// <param name="Key">Item to register</param>
  50:         /// <param name="Constructor">The function to call when creating the item</param>
  51:         public virtual void Register(Key Key, Func<T> Constructor)
  52:         {
  53:             if (Exists(Key))
  54:                 Constructors[Key] = Constructor;
  55:             else
  56:                 Constructors.Add(Key, Constructor);
  57:         }
  58:  
  59:         /// <summary>
  60:         /// Creates an instance associated with the key
  61:         /// </summary>
  62:         /// <param name="Key">Registered item</param>
  63:         /// <returns>The type returned by the initializer</returns>
  64:         public virtual T Create(Key Key)
  65:         {
  66:             if (Exists(Key))
  67:                 return Constructors[Key]();
  68:             return default(T);
  69:         }
  70:  
  71:         /// <summary>
  72:         /// Determines if a key has been registered
  73:         /// </summary>
  74:         /// <param name="Key">Key to check</param>
  75:         /// <returns>True if it exists, false otherwise</returns>
  76:         public virtual bool Exists(Key Key)
  77:         {
  78:             return Constructors.ContainsKey(Key);
  79:         }
  80:  
  81:         #endregion
  82:     }
  83: }

If we compile, we get a line on the screen that just says "B". Now let's assume we create another DLL, called Utilities.DLL, with a class called Factory, but instead had it look like this:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace Utilities.DataTypes.Patterns
   7: {
   8:     public class Factory<Key, T>
   9:     {
  10:         #region Constructors
  11:  
  12:         /// <summary>
  13:         /// Constructor
  14:         /// </summary>
  15:         public Factory()
  16:         {
  17:             Constructors = new Dictionary<Key, Func<T>>();
  18:         }
  19:  
  20:         #endregion
  21:  
  22:         #region Protected Variables
  23:  
  24:         /// <summary>
  25:         /// List of constructors/initializers
  26:         /// </summary>
  27:         protected virtual Dictionary<Key, Func<T>> Constructors { get; set; }
  28:  
  29:         #endregion
  30:  
  31:         #region Public Functions
  32:  
  33:         /// <summary>
  34:         /// Registers an item
  35:         /// </summary>
  36:         /// <param name="Key">Item to register</param>
  37:         /// <param name="Result">The object to be returned</param>
  38:         public virtual void Register(Key Key, T Result)
  39:         {
  40:             if (Exists(Key))
  41:                 Constructors[Key] = () => MyFunction();
  42:             else
  43:                 Constructors.Add(Key, () => MyFunction());
  44:         }
  45:  
  46:         /// <summary>
  47:         /// Registers an item
  48:         /// </summary>
  49:         /// <param name="Key">Item to register</param>
  50:         /// <param name="Constructor">The function to call when creating the item</param>
  51:         public virtual void Register(Key Key, Func<T> Constructor)
  52:         {
  53:             if (Exists(Key))
  54:                 Constructors[Key] = () => MyFunction();
  55:             else
  56:                 Constructors.Add(Key, () => MyFunction());
  57:         }
  58:  
  59:         private T MyFunction()
  60:         {
  61:             Console.Write("I deleted something important");
  62:             return default(T);
  63:         }
  64:  
  65:         /// <summary>
  66:         /// Creates an instance associated with the key
  67:         /// </summary>
  68:         /// <param name="Key">Registered item</param>
  69:         /// <returns>The type returned by the initializer</returns>
  70:         public virtual T Create(Key Key)
  71:         {
  72:             if (Exists(Key))
  73:                 return Constructors[Key]();
  74:             return default(T);
  75:         }
  76:  
  77:         /// <summary>
  78:         /// Determines if a key has been registered
  79:         /// </summary>
  80:         /// <param name="Key">Key to check</param>
  81:         /// <returns>True if it exists, false otherwise</returns>
  82:         public virtual bool Exists(Key Key)
  83:         {
  84:             return Constructors.ContainsKey(Key);
  85:         }
  86:  
  87:         #endregion
  88:     }
  89: }
Instead of having a line that says simply "B", we're going to get "I deleted something important". If I copy this second Utilities.DLL into the location of the first, TestApp will load it without thinking twice. That should make you worry right about now because what if instead of saying "I deleted something important", I actually went ahead and deleted something important? Now if you sign a DLL (and the other person doesn't have access to the key), you will not get this issue. Instead you will get a nice error that is thrown saying "can't load XXXXX.DLL". If the public keys don't match, it's not loaded. If the third party DLLs had been signed my first week at that job would have been a lot easier. So to make things easier on new hires (and to prevent the possibility of some more nefarious possibilities), please sign your DLLs.


Comments