CA2117: APTCA types should only extend APTCA base types

Note

This article applies to Visual Studio 2015. If you're looking for the latest Visual Studio documentation, see Visual Studio documentation. We recommend upgrading to the latest version of Visual Studio. Download it here

Item Value
TypeName AptcaTypesShouldOnlyExtendAptcaBaseTypes
CheckId CA2117
Category Microsoft.Security
Breaking Change Breaking

Cause

A public or protected type in an assembly with the System.Security.AllowPartiallyTrustedCallersAttribute attribute inherits from a type declared in an assembly that does not have the attribute.

Rule Description

By default, public or protected types in assemblies with strong names are implicitly protected by an Inheritance Demands for full trust. Strong-named assemblies marked with the AllowPartiallyTrustedCallersAttribute (APTCA) attribute do not have this protection. The attribute disables the inheritance demand. This makes exposed types declared in the assembly inheritable by types that do not have full trust.

When the APTCA attribute is present on a fully trusted assembly, and a type in the assembly inherits from a type that does not allow partially trusted callers, a security exploit is possible. If two types T1 and T2 meet the following conditions, malicious callers can use the type T1 to bypass the implicit full trust inheritance demand that protects T2:

  • T1 is a public type declared in a fully trusted assembly that has the APTCA attribute.

  • T1 inherits from a type T2 outside its assembly.

  • T2's assembly does not have the APTCA attribute and, therefore, should not be inheritable by types in partially trusted assemblies.

    A partially trusted type X can inherit from T1, which gives it access to inherited members declared in T2. Because T2 does not have the APTCA attribute, its immediate derived type (T1) must satisfy an inheritance demand for full trust; T1 has full trust and therefore satisfies this check. The security risk is because X does not participate in satisfying the inheritance demand that protects T2 from untrusted subclassing. For this reason, types with the APTCA attribute must not extend types that do not have the attribute.

    Another security issue, and perhaps a more common one, is that the derived type (T1) can, through programmer error, expose protected members from the type that requires full trust (T2). When this occurs, untrusted callers gain access to information that should be available only to fully trusted types.

How to Fix Violations

If the type reported by the violation is in an assembly that does not require the APTCA attribute, remove it.

If the APTCA attribute is required, add an inheritance demand for full trust to the type. This protects against inheritance by untrusted types.

It is possible to fix a violation by adding the APTCA attribute to the assemblies of the base types reported by the violation. Do not do this without first conducting an intensive security review of all code in the assemblies and all code that depends on the assemblies.

When to Suppress Warnings

To safely suppress a warning from this rule, you must ensure that protected members exposed by your type do not directly or indirectly allow untrusted callers to access sensitive information, operations, or resources that can be used in a destructive manner.

Example

The following example uses two assemblies and a test application to illustrate the security vulnerability detected by this rule. The first assembly does not have the APTCA attribute and should not be inheritable by partially trusted types (represented by T2 in the previous discussion).

using System;
using System.Security;
using System.Security.Permissions;
using System.Reflection;

// This code is compiled into a strong-named assembly
// that requires full trust. 

namespace AptcaTestLibrary
{
   public class ClassRequiringFullTrustWhenInherited
   {
      // This field should be overridable by fully trusted derived types.
      protected static string location = "shady glen";
     
      // A trusted type can see the data, but cannot change it.
      public virtual string TrustedLocation 
      {
         get 
         {
            return location;
         }
      }
   }
}

Example

The second assembly, represented by T1 in the previous discussion, is fully trusted and allows partially trusted callers.

using System;
using System.Security;
using System.Security.Permissions;
using System.Reflection;

// This class is compiled into an assembly that executes with full 
// trust and allows partially trusted callers. 

// Violates rule: AptcaTypesShouldOnlyExtendAptcaBaseTypes.

namespace AptcaTestLibrary
{
   public class InheritAClassRequiringFullTrust: 
      ClassRequiringFullTrustWhenInherited
   {
      private DateTime meetingDay = DateTime.Parse("February 22 2003");

      public override string ToString() 
      {
         // Another error:
         // This method gives untrusted callers the value 
         // of TrustedLocation. This information should 
         // only be seen by trusted callers.
         string s = String.Format(
            "Meet at the {0} {1}!", 
            this.TrustedLocation, meetingDay.ToString());
         return s;
      }
   }
}

Example

The test type, represented by X in the previous discussion, is in a partially trusted assembly.

using System;
using AptcaTestLibrary;

// If this test application is run from the local machine, 
//  it gets full trust by default.
// Remove full trust.
[assembly: System.Security.Permissions.PermissionSetAttribute(
   System.Security.Permissions.SecurityAction.RequestRefuse, Name = "FullTrust")]

namespace TestSecLibrary
{
    class InheritFromAFullTrustDecendent : ClassRequiringFullTrust
    {
        public InheritFromAFullTrustDecendent()
        {
            // This constructor maliciously overwrites the protected 
            // static member in the fully trusted class.
            // Trusted types will now get the wrong information from 
            // the TrustedLocation property.
            InheritFromAFullTrustDecendent.location = "sunny meadow";
        }

        public override string ToString()
        {
            return InheritFromAFullTrustDecendent.location;
        }
    }

    class TestApctaInheritRule
    {
        public static void Main()
        {
            ClassRequiringFullTrust iclass =
               new ClassRequiringFullTrust();
            Console.WriteLine(iclass.ToString());

            // You cannot create a type that inherits from the full trust type
            // directly, but you can create a type that inherits from 
            // the APTCA type which in turn inherits from the full trust type.

            InheritFromAFullTrustDecendent inherit =
               new InheritFromAFullTrustDecendent();
            //Show the inherited protected member has changed.
            Console.WriteLine("From Test: {0}", inherit.ToString());

            // Trusted types now get the wrong information from 
            // the TrustedLocation property.
            Console.WriteLine(iclass.ToString());
        }
    }
}

This example produces the following output.

Meet at the shady glen 2/22/2003 12:00:00 AM! From Test: sunny meadow Meet at the sunny meadow 2/22/2003 12:00:00 AM!

CA2116: APTCA methods should only call APTCA methods

See Also

Secure Coding Guidelines .NET Framework Assemblies Callable by Partially Trusted Code Using Libraries from Partially Trusted Code Inheritance Demands