DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 14.9 Making a Security Assert Safe

Problem

You want to assert that at a particular point in the call stack, a given permission is understood to be available for all subsequent calls. However, doing this can easily open a security hole to allow other malicious code to spoof your code or to create a back door into your component. You want to assert a given security permission, but you want to do so in a secure and efficient manner.

Solution

In order to make this approach secure, we need to call Demand on the permissions that the subsequent calls need and on which we are using Assert in order to make sure that code that doesn't have these permissions can't slip by due to the Assert. This is demonstrated by the function CallSecureFunctionSafelyAndEfficiently, which performs a Demand, then an Assert before calling into SecureFunction, which performs a Demand for a ReflectionPermission.

The code listing for CallSecureFunctionSafelyAndEfficiently is:

public static void CallSecureFunctionSafelyAndEfficiently( )
{

    // set up a permission to be able to access nonpublic members 
    // via reflection
    ReflectionPermission perm = 
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);

    // Demand the permission set we have compiled before using Assert
    // to make sure we have the right before we Assert it.  We do
    // the Demand to insure that we have checked for this permission
    // before using Assert to short-circuit stackwalking for it, which
    // helps us stay secure, while performing better.
    perm.Demand( );

    // Assert this right before calling into the function that
    // would also perform the Demand to short-circuit the stack walk
    // each call would generate.  The Assert helps us to optimize
    // out use of SecureFunction
    perm.Assert( );

    // We call the secure function 100 times but only generate
    // the stackwalk from the function to this calling function
    // instead of walking the whole stack 100 times.
    for(int i=0;i<100;i++)
    {
        SecureFunction( );
    }
}

The code listing for SecureFunction is shown here:

public static void SecureFunction( )
{
    // set up a permission to be able to access nonpublic members 
    // via reflection
    ReflectionPermission perm = 
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);

    // Demand the right to do this and cause a stackwalk
    perm.Demand( );            

    // Perform the action here...
}

Discussion

In our demonstration function CallSecureFunctionSafelyAndEfficiently, the function we are calling (SecureFunction) performs a Demand on a ReflectionPermission to ensure that the code can access nonpublic members of classes via reflection. Normally, this would result in a stackwalk for every call to SecureFunction. The Demand in CallSecureFunctionSafelyAndEfficiently is only there to protect against the usage of the Assert in the first place. To make this more efficient, we can use Assert to state that all functions called from this one issuing Demands do not have to stack walk any further as the Assert says stop checking for this permission in the call stack. In order to do this, you need the permission to call Assert.

The problem comes in with this Assert as it opens up a potential luring attack where SecureFunction is called via CallSecureFunctionSafelyAndEfficiently, which calls Assert to stop the Demand stack walks from SecureFunction. If unauthorized code without this ReflectionPermission were able to call CallSecureFunctionSafelyAndEfficiently, the Assert would prevent the SecureFunction Demand call from determining that there is some code in the call stack without the proper rights. This is the beauty of the call stack—checking in the CLR when a Demand occurs.

In order to protect against this, we issue a Demand for the ReflectionPermission needed by SecureFunction in CallSecureFunctionSafelyAndEfficiently to close this hole before issuing the Assert. The combination of this Demand and the Assert causes us to do one stack walk instead of the original 100 that would have been caused by the Demand in SecureFunction but to still maintain secure access to this functionality.

Security optimization techniques, such as using Assert, in this case (even though it isn't the primary reason to use Assert), can help class library and controls developers that are trusted to perform Asserts in order to speed the interaction of their code with the runtime; but if used improperly, these techniques can also open up holes in the security picture as well. This example shows that you can have both performance and security where secure access is concerned.

If you are using Assert, be mindful that stackwalk overrides should never be made in a class constructor. Constructors are not guaranteed to have any particular security context, nor are they guaranteed to execute at a specific point in time. This lack leads to the call stack not being well-defined, and Assert used here can produce unexpected results.

One other thing to remember with Assert is that you can only have one active Assert in a function at a given time. If you Assert the same permission twice, a SecurityException is thrown by the CLR. You must revert the original Assert first using RevertAssert and then you can declare the second Assert.

You might have the idea that declarative demands would be faster due to the CLR's knowledge of the call stack; shouldn't it be able to perform the optimization we have done manually here? In the 1.0 and 1.1 versions of the CLR, it turns out that declarative demands are actually slower, since this optimization does not occur.

See Also

See the "CodeAccessSecurity.Assert Method," "CodeAccessSecurity.Demand Method," "CodeAccessSecurity.RevertAssert Method," and "Overriding Security Checks" topics in the MSDN documentation.

    [ Team LiB ] Previous Section Next Section