ASP.NET VERSIONS: 1.1
Walking the Stack
Permissions from the Caller s Perspective
By Don Kiely
In the last column (Protecting Resources with Permission Objects), I took a brief look at .NET permissions and how you might use them when your code is responsible for protecting a valuable resource. It all boils down to writing the code that tells the CLR to make sure that whoever calls the code to access the resource has a certain type of permission. As we saw last time, most often the called code instantiates a permission of the appropriate type and calls that permission object s Demand method. That causes the CLR to set off to check the call stack to make sure that everyone in the stack has the specified permission. There are lots of variations on how you can do that, including using attributes rather than writing code, but that s the basic idea.
This time around I ll look at permissions from the perspective of the calling code. The crucial concept here is that when a piece of code Demands a permission, the CLR stops execution and walks the stack to make sure that every caller in the stack has the required permission (unless the walk is modified in some way, which I ll discuss later in this column). The purpose of the stack walk is to prevent luring attacks, in which an attacker attempts to con trusted code into making a call to other code that protects a resource in an attempt to get illicit access to the resource. This is an extremely common attack, justifying all of Microsoft s hard work to implement these security features.
Let s look at an example, which I ve taken from a code-generation utility that a friend wrote. In the image below, I show the call stack when the code is somewhere in the AppendIfExists procedure (see Figure 1). Let s say that this procedure is appending something to a file in a protected location on the disk. That code might Demand a FileIOPermission. As soon as AppendIfExists calls that method, the CLR starts walking the stack. In this case it checks the GetAllStoredProcXML procedure first. Does that have the proper FileIOPermission permission? Yes, it does, so the CLR continues its walk. Does the CreateMetaData have the required permission? Yes, it does. Then it checks the Run method, which happens to reside in another component. Maybe that component is running from some location across the intranet, and local policy on the machine where AppendIfExists is running doesn t trust intranet applications very much. Oops! Say it finds that the Run method doesn t have the necessary FileIOPermission. At that point the CLR stops the stack walk and throws a security exception so you d better have code somewhere that handles that exception gracefully!
Figure 1: The call stack when the code is somewhere in the AppendIfExists procedure.
Most of the time when called code demands a permission, this is the scenario that plays out hundreds or thousands of times during typical program execution. You might be concerned about performance, and rightly so. This is a lot of cycles that it is burning up, but Microsoft did a lot of work to optimize the process. I believe that it is a trivial price to pay for the security it gives .NET applications.
But what if you want to influence the stack walk? Say you know for a fact, because this code only executes in very controlled situations, that whoever calls CreateMetaData is trusted. Is there any way to circumvent the stack walk?
Of course there is! This is .NET, after all! System.Security.CodeAccessPermission, the mother of most permission objects in .NET, exposes four important methods that calling code can use to modify how the CLR performs its stack walk:
- Assert. Asserts that callers of the caller, which is executing the Assert method, have the required permission.
- RevertAssert. Can shorten duration of an Assert, by causing any previous Assert to be removed and no longer in effect.
- Deny. Fails a stack walk of the specified permission, preventing code higher in the stack to access the protected resource.
- PermitOnly. Fails any non-matching stack walk.
These can be a bit confusing, but it s not as bad as it seems. The good news is that you ll rarely need to use any of them. Even when you do, you re most likely to use Assert. This method, called in any method up the call stack from the code protecting the resource, effectively says, CLR, you look tired. Let me do you a favor. I trust all the code that calls me, as well as any code that calls the code that calls me, all the way up the stack. So you don t need to do all that work of checking all that other code for the required permissions. You go back now, and let that code that accesses that protected file execute, so I can get my work done.
That should be a scary scenario! What we ve done, assuming that the CLR honors the Assert call, is circumvent the protection built into .NET to prevent luring attacks. If some code in the call stack isn t as saintly as the Assert caller asserts, you have a security breach. So it is only in the rarest circumstances that you should use Assert. When you do, you are taking full responsibility for ensuring that a luring attack is impossible through this security hole you ve opened.
Fortunately, the .NET Framework provides protection against malicious use of Assert. The code that calls Assert must itself have the SecurityPermission permission with the SecurityPermissionFlag.Assertion flag set. This is a dangerous permission to have; if you ve carefully designed your app to have only the permissions it absolutely requires, it shouldn t have this permission. In the pre-defined ASP.NET trust levels, only the Full, High, and Medium trust levels have the Assertion form of SecurityPermission.
The other three methods are fairly esoteric refinements of the stack walk and Assert. The RevertAssert method lets you limit the damage potentially done by using Assert by removing the Assert as soon as you no longer need it. So you might call Assert immediately before calling the method that accesses the protected resource, then call RevertAssert immediately thereafter. That way, any other code in the caller that executes thereafter will have a normal stack walk. Deny and PermitOnly are rare enough that there s a good chance you won t have a need for them in your .NET programming career.
I have to emphasize an important point here: This discussion assumes that the calling code is not running with Full Trust. Fully trusted code is dangerous code. I know that it s more work to write initially, but that is a tiny amount of effort compared to the risk you re exposing your users to, the time you ll spend on the help desk when your code is used in hacker attacks, and the angst you ll feel at being uncool. I ve written a lot about this topic and least privilege here in asp.netNOW and in my blog at http://www.sqljunkies.com/weblog/donkiely/. There are a lot of resources available to help you with the job!
Don Kiely is senior technology consultant for Information Insights, a business and technology consultancy in Fairbanks, AK. E-mail him at mailto:[email protected].