SYSK 259: Is ‘internal’ class modifier a sufficient level of protection?

So, say you have a class that you’d like to make sure that nobody outside of your company (or your product/project team) can use. As an example, I’ll use an encryption class (see below) with Encrypt and Decrypt methods:

internal class Encryption

{

    internal string Encrypt(string input)

    {

        return "Encrypted...";

    }

    internal string Decrypt(string input)

    {

        return "Decrypted...";

    }

}

You might think that using internal class and method modifiers are sufficient security… After all, ildasm would show the class as private, and attempting to instantiate this class from another assembly would result in a compile time error 'ClassLibrary1.Encryption' is inaccessible due to its protection level.

 

However, with same ildasm tool, you can get the signature of internal, private and protected methods… not just public. With that, if you’re willing to write a bit of code using reflection, you can easily bypass the compile time error. E.g. the code below returns the expected value of “Encrypted…”:

object x = Activator.CreateInstanceFrom("ClassLibrary1.dll", "ClassLibrary1.Encryption").Unwrap();

string result = x.GetType().InvokeMember("Encrypt",

        System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,

        null, x, new object[] { "string to be encrypted" }) as string;

And now, if a hacker got a hold of your server, he/she doesn’t have to decrypt your sensitive data… they only need to invoke your code that’ll do it for them…

Same is true not only for your internal classes, but also internal and even private classes within the .NET framework (see the bottom of this post for a sample).

So, what’s my point? Using class and method modifiers like internal or private is insufficient for protecting it from being invoked by unauthorized applications/users. In my projects I always add evidence based security for sensitive classes/methods, e.g.:

[StrongNameIdentityPermissionAttribute(SecurityAction.LinkDemand, PublicKey = YourCompany.YourProject.PublicKey)]

public string Encrypt(string message)

Appendix

Below is a snippet of code that instantiates and uses an internal .NET class (note: undocumented classes are not guaranteed to be preserved or work same way in future version. Do not use them!):

System.Reflection.Assembly assmbly = typeof(System.IO.Compression.GZipStream).Assembly;

Type t = assmbly.GetType("System.IO.Compression.FastEncoder");

System.Reflection.ConstructorInfo ci = t.GetConstructor(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public,

    null, new Type[] { typeof(bool) }, new System.Reflection.ParameterModifier[] { });

object fastEnc = null;

if (ci != null)

    fastEnc = ci.Invoke(new object[] { false }); // false = !GZip

else

    System.Diagnostics.Debug.Assert(false, string.Format("Unable to instanciate {0} class", t.Name));

byte[] input = System.Text.Encoding.Unicode.GetBytes("Source data");

int startIndex = 0;

int count = input.Length;

t.InvokeMember("SetInput",

    System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public,

    null, fastEnc, new object[] { input, startIndex, count });

byte[] outputBuffer = new byte[2048];

int charactersWritten = (int)t.InvokeMember("GetCompressedOutput",

    System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public,

    null, fastEnc, new object[] { outputBuffer });

System.Reflection.Assembly assmbly = System.Reflection.Assembly.LoadFrom("WindowsApplication1.exe");

Type encType = assmbly.GetType("WindowsApplication1.Encryption");

System.Reflection.ConstructorInfo ci = encType.GetConstructor(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public,

    null, new Type[] { }, new System.Reflection.ParameterModifier[] { });

object enc = ci.Invoke(new object[] { });

string encString = (string) encType.InvokeMember("Encrypt",

    System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,

    null, enc, new object[] { "string to be encrypted" });

string decString = (string) encType.InvokeMember("Decrypt",

    System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,

    null, enc, new object[] { encString });