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.
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... }
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.