Securing Wrapper Code

Caution

Code Access Security (CAS) and Partially Trusted Code

The .NET Framework provides a mechanism for the enforcement of varying levels of trust on different code running in the same application called Code Access Security (CAS).

CAS is not supported in .NET Core, .NET 5, or later versions. CAS is not supported by versions of C# later than 7.0.

CAS in .NET Framework should not be used as a mechanism for enforcing security boundaries based on code origination or other identity aspects. CAS and Security-Transparent Code are not supported as a security boundary with partially trusted code, especially code of unknown origin. We advise against loading and executing code of unknown origins without putting alternative security measures in place. .NET Framework will not issue security patches for any elevation-of-privilege exploits that might be discovered against the CAS sandbox.

This policy applies to all versions of .NET Framework, but does not apply to the .NET Framework included in Silverlight.

Wrapper code, especially where the wrapper has higher trust than code that uses it, can open a unique set of security weaknesses. Anything done on behalf of a caller, where the caller's limited permissions are not included in the appropriate security check, is a potential weakness to be exploited.

Never enable something through the wrapper that the caller could not do itself. This is a special danger when doing something that involves a limited security check, as opposed to a full stack walk demand. When single-level checks are involved, interposing the wrapper code between the real caller and the API element in question can easily cause the security check to succeed when it should not, thereby weakening security.

Delegates

Delegate security differs between versions of the .NET Framework. This section describes the different delegate behaviors and associated security considerations.

In version 1.0 and 1.1 of the .NET Framework

Version 1.0 and 1.1 of the .NET Framework perform the following security actions against a delegate creator and a delegate caller.

  • When a delegate is created, security link demands on the delegate target method are performed against the grant set of the delegate creator. Failure to satisfy the security action results in a SecurityException.

  • When the delegate is invoked, any existing security demands on the delegate caller are performed.

Whenever your code takes a Delegate from less-trusted code that might call it, make sure that you are not enabling the less-trusted code to escalate its permissions. If you take a delegate and use it later, the code that created the delegate is not on the call stack and its permissions will not be tested if code in or under the delegate attempts a protected operation. If your code and the caller code have higher privileges than the creator, the creator can orchestrate the call path without being part of the call stack.

In version 2.0 and later versions of the .NET Framework

Unlike previous versions, version 2.0 and later versions of the .NET Framework performs security action against the delegate creator when the delegate is created and called.

  • When a delegate is created, security link demands on the delegate target method are performed against the grant set of the delegate creator. Failure to satisfy the security action results in a SecurityException.

  • The delegate creator's grant set is also captured during delegate creation and stored with the delegate.

  • When the delegate is invoked, the delegate creator's captured grant set is first evaluated against any demands in the current context if the delegate creator and caller belong to different assemblies. Next, any existing security demands on the delegate caller are performed.

A special protection case with link demands has been strengthened in the security infrastructure, but it is still a source of possible weakness in your code.

If fully trusted code calls a property, event, or method protected by a LinkDemand, the call succeeds if the LinkDemand permission check for the caller is satisfied. Additionally, if the fully trusted code exposes a class that takes the name of a property and calls its get accessor using reflection, that call to the get accessor succeeds even though the user code does not have the right to access this property. This is because the LinkDemand checks only the immediate caller, which is the fully trusted code. In essence, the fully trusted code is making a privileged call on behalf of user code without making sure that the user code has the right to make that call.

To help prevent such security holes, the common language runtime extends the check into a full stack-walking demand on any indirect call to a method, constructor, property, or event protected by a LinkDemand. This protection incurs some performance costs and changes the semantics of the security check; the full stack-walk demand might fail where the faster, one-level check would have passed.

Assembly loading wrappers

Several methods used to load managed code, including Assembly.Load, load assemblies with the evidence of the caller. If you wrap any of these methods, the security system could use your code's permission grant, instead of the permissions of the caller to your wrapper, to load the assemblies. Don't allow less-trusted code to load code that is granted higher permissions than those of the caller to your wrapper.

Any code that has full trust or significantly higher trust than a potential caller (including an Internet-permissions-level caller) could weaken security in this way. If your code has a public method that takes a byte array and passes it to Assembly.Load, thereby creating an assembly on the caller's behalf, it might break security.

This issue applies to the following API elements:

Demand vs. LinkDemand

Declarative security offers two kinds of security checks that are similar but perform different checks. It's good to understand both forms, because the wrong choice can result in weak security or performance loss.

Declarative security offers the following security checks:

  • Demand specifies the code access security stack walk. All callers on the stack must have the specified permission or identity to pass. Demand occurs on every call because the stack might contain different callers. If you call a method repeatedly, this security check occurs each time. Demand is good protection against luring attacks; unauthorized code trying to get through it will be detected.

  • LinkDemand happens at just-in-time (JIT) compilation time and checks only the immediate caller. This security check does not check the caller's caller. Once this check passes, there is no additional security overhead no matter how many times the caller might call. However, there is also no protection from luring attacks. With LinkDemand, any code that passes the test and can reference your code can potentially break security by allowing malicious code to call using the authorized code. Therefore, do not use LinkDemand unless all the possible weaknesses can be thoroughly avoided.

    Note

    In the .NET Framework 4, link demands have been replaced by the SecurityCriticalAttribute attribute in Level2 assemblies. The SecurityCriticalAttribute is equivalent to a link demand for full trust; however, it also affects inheritance rules. For more information about this change, see Security-Transparent Code, Level 2.

The extra precautions required when using LinkDemand must be programmed individually; the security system can help with enforcement. Any mistake opens a security weakness. All authorized code that uses your code must be responsible for implementing additional security by doing the following:

  • Restricting the calling code's access to the class or assembly.

  • Placing the same security checks on the calling code that appear on the code being called and obligating its callers to do so. For example, if you write code that calls a method that is protected with a LinkDemand for the SecurityPermission with the UnmanagedCode flag specified, your method should also make a LinkDemand (or Demand, which is stronger) for this permission. The exception is if your code uses the LinkDemand-protected method in a limited way that you decide is safe, given other security protection mechanisms (such as demands) in your code. In this exceptional case, the caller takes responsibility in weakening the security protection on the underlying code.

  • Ensuring that your code's callers cannot trick your code into calling the protected code on their behalf. In other words, callers cannot force the authorized code to pass specific parameters to the protected code, or to get results back from it.

If a virtual method, property, or event with LinkDemand overrides a base class method, the base class method must also have the same LinkDemand for the overridden method in order to be effective. It is possible for malicious code to cast back to the base type and call the base class method. Also note that link demands can be added implicitly to assemblies that do not have the AllowPartiallyTrustedCallersAttribute assembly-level attribute.

It is a good practice to protect method implementations with link demands when interface methods also have link demands. Note the following about using link demands with interfaces:

  • If you place a LinkDemand on a public method of a class that implements an interface method, the LinkDemand will not be enforced if you then cast to the interface and call the method. In this case, because you linked against the interface, only the LinkDemand on the interface is honored.

Review the following items for security issues:

  • Explicit link demands on interface methods. Make sure these link demands offer the expected protection. Determine whether malicious code can use a cast to get around the link demands as described previously.

  • Virtual methods with link demands applied.

  • Types and the interfaces they implement. These should use link demands consistently.

See also