Marking Your Code Transparent

Last week I discussed the concepts of security transparency and security critical code.  Now it's time to get into the how-to's

Marking an Entire Assembly Critical

This is by far the easiest of the operations ... just do nothing [:D].  By default, all assemblies compiled for the v1.x and v2.0 frameworks are security critical and contain only critical code.  However, this default may change in the future.  It's possible that when we release the next framework we'll decide to make assemblies compiled for that version transparent by default.

Marking an Entire Assembly Transparent

The next easiest operation is marking an entire assembly as containing only security transparent code. To do this, simply mark the assembly with the System.Security.SecurityTransparentAttributeAttribute:

[assembly: SecurityTransparent]

This has the effect of saying that the assembly may not contain any critical code, and therefore will not elevate the privileges of the call stack due to the restrictions mentioned in the last article. 

Mixing Critical and Transparent Code in the Same Assembly

A more common scenario arises when you want to mix critical and transparent code in the same assembly.  In that case you start by marking the assembly with the System.Security.SecurityCriticalAttribute:

[assembly: SecurityCritical]

What this says is somewhat counter-intuitive.  By marking the assembly with the SecurityCriticalAttribute, you're saying that the assembly can contain critical code, however unless explicitly marked all code within the assembly will default to being transparent.  In other words, if you want to perform security critical actions, you'll need explicitly mark the code that will perform the critical action with another SecurityCritical attribute.

When you're marking the critical section of code with its SecurityCritical attribute, you have two options.  The first choice is to mark a block of code (such as a method or property getter or setter) with a SecurityCriticalAttribute which says that the marked method contains critical code.  Your other option is to mark something that contains several blocks of code (such as an assembly with several methods) with the SecurityCriticalAttribute passing it a parameter of SecurityCriticalScope.Everything.  That has the effect of saying all blocks of code under this scope are critical.


Examples

Some examples may help clear things up:

 Lets start with:

public class A
{
    public void Foo()
    {
        // critical
    }

    public int Bar
    {
        get { /* critical */ }
        set { /* critical */ }
    }
}

public class B
{
    internal string Baz
    {
        get { /* critical */ }
        set { /* critical */ }
    }
}

This assembly contains only critical code, since there are no SecurityTransparent or SecurityCritical attributes on the assembly level.  This is the default configuration, meaning if you don't explicitly start to use transparency, there will be no restrictions on what your code can do.  If we wanted everything to be transparent:

[assembly: SecurityTransparent]

public class A
{
    public void Foo()
    {
         // transparent
    } 

    public int Bar
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

public class B
{
    internal string Baz
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

This assembly is marked with the SecurityTransparentAttribute.  No further work is needed -- because the assembly is marked transparent, all of its code is also transparent.  Note that this assembly:

[assembly: SecurityCritical]

public class A
{
    public void Foo()
    {
        // transparent
    }

    public int Bar
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

public class B
{
    internal string Baz
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

Also is entirely transparent, even though it was marked with the SecurityCritical attribute.  The reason is that SecurityCritical means the assembly may contain critical code.  But since none of the code in the assembly is marked critical, it all defaults to being transparent.  In this assembly however:

[assembly: SecurityCritical]

public class A
{
    [SecurityCritical]
    public void Foo()
    {
        // critical
    }

    public int Bar
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

public class B
{
    internal string Baz
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

Method A::Bar is marked with a separate SecurityCritical attribute.  Combined with the assembly level SecurityCriticalAttribute, this means that all the code in the assembly is transparent with the exception of A::Bar, which is critical.  Similarly:

[assembly: SecurityCritical]

public class A
{
    public void Foo()
    {
        // transparent
    }

    public int Bar
    {
        [SecurityCritical]
        get { /* critical */ }
        set { /* transparent */ }
    }
}

public class B
{
    internal string Baz
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

This assembly is entirely transparent with the exception of the property getter for A::Bar.  Notice that the SecurityCriticalAttribute goes on the getter and setter of the property, not on the property declaration itself.  In this case, only the getter is critical since the setter was not marked with its own SecurityCriticalAttribute.  Finally:

[assembly: SecurityCritical]

[SecurityCritical(SecurityCriticalScope.Everything)]
public class A
{
    public void Foo()
    {
        // critical
    }

    public int Bar
    {
        get { /* critical */ }
        set { /* critical */  }
    }
}

public class B
{
    internal string Baz
    {
        get { /* transparent */ }
        set { /* transparent */ }
    }
}

In this assembly all of the code that appears as a part of class A is critical, since A was marked with the SecurityCriticalAttribute with a scope of Everything.