WEBINAR:On-Demand

Introduction

What's stopping people from using any assemblies you've written?

This is one of the first questions that I asked myself about .NET. In a well-organised piece of software, anything useful is designed to be shared—not just in one application, but in all applications written by a particular company. The way of doing this is normally by the use of shared code. Prior to .NET, this generally occurred using either Win32 DLLs or COM DLLs. In .NET, the same philosophy of shared code comes in the form of assemblies, which are again DLLs.

Why use this philosophy of shared DLLs as opposed to copying classes around and including them in projects? The one advantage is that the DLL (whether it be Win32, COM, or .NET assembly) can be tested and proved to be correct. Once proven to be correct, any company then can use that DLL with confidence; this speeds up development and also leads to more stable solutions. Another advantage is that if a bug is found in the DLL, a fix can be made and the fix will automatically filter down through all the affected applications.

.NET assemblies can be accessed by any application, leading to the possibility of other people utilising your company's development effort without cost. This accessabiilty also cause can a potential breach of security. Someone, for instance, could write an assembly with the same interface (in other words, the same classes and so forth), and replace the original assembly. Such a method would give the new assembly access to all data passed to it from the application. If this new assembly then calls the original assembly, the application would be unaware of this breach of security.

It's no surprise that it is the System.Security namespace within the .NET Framework that provides some of the answers to the above problems.

Using Strong Names

The primary way to protect your assemblies from attack is to attach a strong name. Strong names are pairs of keys (strings of numbers)—one private and one public. The private key is held inside the assembly and is inaccessible. The public key is available to all. Generally speaking, a public/private key pair is used in encryption: A public key is used to encrypt a data stream, but you can only decrypt this data if you have the private key.

You generate a public/private key pair by use of the sn.exe utility, which generates a key pair and stores it into a file. This utility is a part of the .NET Framework SDK, so you most likely already have it if you are doing .NET development.

You use the strong naming tool as shown below:

sn -k <keyfilename>.snk

To attach a strong named key file to an assembly (either a DLL or an executable), go to the AssemblyInfo file and replace the AssemblyKeyFile attribute with your .snk file. For example,

[assembly: AssemblyKeyFile("mykeyfile.snk")]

If you do this for both your executable and any assemblies, using the same key file, these assemblies cannot be replaced after compilation without linking to the same public/private key pair in the SNK file. This, then, prevents access to your applications data by replacing the appropriate assembly with one that traps the data passing through it.

Code Access Attributes

There are a number of attributes that you can attach to your classes inside of assemblies to prevent applications from using them. All are derived from the System.Security.Permissions.CodeAccessSecurityAttribute. Here, I will give an example of one of them: the StrongNameIdentityPermissionAttribute. To use this attribute, you first need to be able to extract the public key out of the .snk file. You do this by the use of the Secutil.exe tool.

You will need access to this public key, so I recommend you pipe the output into a file you then can open in NotePad, for example:

secutil -hex -c -s <yourassembly>.dll > key.txt

If you have a class in an assembly that you don't want anyone else to be able to access, you can use the StrongNameIdentityPermissionAttribute to prevent access. Any calling code that isn't signed with your .snk file in the following way—using the public key from above as an example—won't have access:

This attribute can be added at either the class level or the method level. In addition, you can choose when you want your attribute to be tested: MSDN lists quite a few alternatives here. You choose by passing in the appropriate SecurityAction enumerated value. I should let you know that I've only been able to get two to work, SecurityAction.Demand and securityAction.LinkDemand.

SecurityAction.Demand—This tests the condition every time either the class is instansiated or every time the method is called, depending on whether a method or a class has the attribute attached.

SecurityAction.LinkDemand—This tests the condition when the code to which the attribute is attached is JIT-ted. This only happens once and so is potentially more efficient.

Conclusion

I have shown how to protect our code both from a hacker attempting to replace an assembly and from someone trying to use one of our assemblies. In fact, there are many more code access attributes that you could use; I've only dealt with one here. In addition, you could write your own code access attributes to test just about anything, but that's a subject for a later date.

The demonstration code included with this article is intended as a testbed to try out various combinations of attributes and see what happens.

About the Author

David McClarnon

He first encountered Windows programming using Visual C++/MFC version 1.5 on Windows 3.11 a very long time ago. He is now a contract developer specialising in .NET/native interop with p/invoke.

Advertiser Disclosure:
Some of the products that appear on this site are from companies from which QuinStreet receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. QuinStreet does not include all companies or all types of products available in the marketplace.

Thanks for your registration, follow us on our social networks to keep up-to-date