I recently came across a security issue while reviewing a client's .NET application. It is an interesting story...
because it demonstrates the power of .NET's code access security and Link Demand specifically.
The application spans multiple assemblies and has a database that contains a lot of important information. To avoid redundant code for database access, the client designated one assembly as the data access assembly. All other assemblies have to go through this data access assembly to get data from the database. This, however, does not assure us of the quality of the input reaching the data access assembly.
The developers had a nice solution for this problem. They placed a validation assembly between the data access assembly and the other client assemblies. All client assemblies go through this validation assembly to reach the data access assembly.
In the above diagram, the four client assemblies go through the validation assembly to reach the data access assembly. That seems to be a nice architecture. Doesn't it? Only good input reaches the data access layer, since all clients go through the validation assembly, and the validation assembly rejects bad input. It works as long as there are no bad assemblies around.
Enter the "bad guy." The bad guy is a malicious assembly -- written by somebody else but running on the same machine -- interested in accessing the database. Notice that the bad guy can bypass the validations and directly call the data access layer. This is a security issue that the developers didn't think of. Now they wanted a solution for it.
How do we prevent this? The solution is to use Link Demand with Strong Names.
Link Demand to the rescue
First, some background: When an assembly requests a Link Demand for some permissions, .NET will verify that the calling assembly has the demanded permissions at link time. Note the Link Demand checks only the permissions of the immediate caller, and it's done at link time.
A Strong Name is a .NET feature that allows assemblies to have unique, cryptographically secure names. You can trust the source and name of a strongly named assembly, as it's signed with a public/private key pair that belongs to the developer.
The private key in the pair is a secret known only to the developer or his organization. The interesting thing is that a Strong Name can be used as an evidence for a permission that's being demanded. To use Strong Names in demands, .NET defines a permission class called StrongNameIdentityPermission. That permission class can be used to declaratively invoke demands with Strong Names.
In our solution, we want the data access assembly to perform a Link Demand to restrict the caller. Now the Link Demand has to be on some evidence that the caller assembly can provide. We could do a Link Demand on the public key of the organization. Since the public keys would be unique to the organization, we can ensure that only assemblies belonging to the organization can call the data access assembly. Here's what the Link Demand would look like:
[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = PublicKeys.MyCompany)]
The above would ensure that only assemblies belonging to the same organization (whose public key is PublicKeys.MyCompany) can invoke the Data access assembly.That looks good to me.
But you might feel that is too lenient considering that the design requires only the validation assembly to call the data access assembly. I think it is OK to trust assemblies of one's own organization and rest assured that they will not indulge in malicious activities. But if you want to be absolutely sure, you need to ensure only the specific assembly that is granted access. That can be done by demanding on the name in addition to the public key of the assembly.
[StrongNameIdentityPermission(SecurityAction.LinkDemand, PublicKey = PublicKeys.MyCompany)] [StrongNameIdentityPermission(SecurityAction.LinkDemand, name = "ValidationAssemblyName")]
The above ensures that only the validation assembly can call the data access assembly. That's very secure.
Some of you might feel that even the first option of trusting only the organization's own assemblies is too strict. There could be real situations when we trust some external assemblies enough to let them call the data access assembly. So how can we allow assemblies belonging to certain trusted vendors also to call the data access assembly?
Link Demand comes to our aid again. What we would like is to have .NET check for a set of allowed public keys. The regular Link Demand we have seen so far can demand only a single public key. There's a slightly different version of Link Demand, called LinkDemandChoice that supports multiple public keys. We can use LinkDemandChoice in this situation.
[StrongNameIdentityPermission(SecurityAction.LinkDemandChoice, PublicKey = PublicKeys.Vendor1)] [StrongNameIdentityPermission(SecurityAction.LinkDemandChoice, PublicKey = PublicKeys.Vendor2)]
Using the above we can restrict which external vendors may call the Data access assembly.
About the author: Sangita Pakala, GCIH, is the lead author of the popular "OWASP Application Security FAQ. Sangita's work has been presented at RSA Conference 2006 and ISACA Europe 2005. She is the delivery manager at Plynt and also the editor of Palisade, the application security magazine.