.NET Framework 2.0 Security Guidelines - Unmanaged Code

From Guidance Share

Jump to: navigation, search

- J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Chaitanya Bijwe


Contents

Use naming conventions (safe, native, unsafe) to identify unmanaged APIs

Unmanaged code represents a significant risk if used improperly from managed code. Apply naming conventions so that you can be reminded of native code risks when you develop, review, or revise code. Categorize your unmanaged code by using a prefix to encapsulate the types of unmanaged APIs. Use the following naming convention.

  • Safe. This identifies code that poses no possible security threat. It is harmless for any code, malicious or otherwise, to call. An example is code that returns the current processor tick count. Safe classes can be annotated with SuppressUnmanagedCodeSecurityAttribute, which turns off the code access security permission demand for full trust.
[SuppressUnmanagedCodeSecurity]
class SafeNativeMethods {
      [DllImport("user32")]
      internal static extern void MessageBox(string text);
}
 
  • Native. This is potentially dangerous unmanaged code, but code that is protected with a full stack walking demand for the unmanaged code permission. These are implicitly made by the interop layer, unless they have been suppressed with SupressUnmanagedCodeSecurityAttribute.
class NativeMethods {
      [DllImport("user32")]
      internal static extern void FormatDrive(string driveLetter);
}
 
  • Unsafe. This is potentially dangerous unmanaged code that has the security demand for the unmanaged code permission declaratively suppressed. These methods are potentially dangerous. Any caller of these methods must do a full security review to make sure that the usage is safe and protected because no stack walk is performed.
[SuppressUnmanagedCodeSecurity]
class UnsafeNativeMethods {
      [DllImport("user32")]
      internal static extern void CreateFile(string fileName);
}
 

Isolate unmanaged API calls in a wrapper assembly

To make it easier maintain and review your code, place all unmanaged API calls in a wrapper assembly. This allows you to:

  • Easily determine the set of unmanaged APIs your application is dependant on.
  • Isolate your unmanaged calls in a single assembly.
  • Isolate the UnmanagedCodePermission to a single assembly.


Constrain and validate string parameters

Certain types of input and data validation vulnerabilities are much more likely to occur in native code than in managed code. To reduce the risk of buffer overrun, format string, and integer overflow bugs, constrain and validate string parameters passed to native code.

Check the length of any input string inside your wrapper code to make sure that it does not exceed the limit defined by the unmanaged API. If the unmanaged API accepts a character pointer, you may not know the maximum permitted string length unless you have access to the unmanaged source.

If you cannot examine the unmanaged code because you do not own it, make sure that you rigorously test the API by passing in deliberately long input strings.

If your code uses a StringBuilder to receive a string passed from an unmanaged API, make sure that it can hold the longest string that the unmanaged API can hand back.


Validate array bounds

Array bounds are not automatically checked in native code. If you use an array to pass input to an unmanaged API, check that the managed wrapper verifies that the capacity of the array is not exceeded.


Check file path lengths

If the unmanaged API accepts a file name and path, check that it does not exceed 260 characters. This limit is defined by the Win32 MAX_PATH constant. It is very common for unmanaged code to allocate buffers of this length to manipulate file paths. In many cases, the native does not check or constrain input to the maximum length.

Note Directory names and registry keys can be a maximum of 248 characters long.


Use the /GS switch to compile unmanaged code

If you own the unmanaged code, use the /GS switch to compile it, which enables stack probes to help detect buffer overflows. For more information about the /GS switch, see Microsoft Knowledge Base article 325483, "WebCast: Compiler Security Checks: The -GS compiler switch" at http://support.microsoft.com/default.aspx?scid=kb;en-us;325483.


Inspect unmanaged code for dangerous APIs

If you have access to the source code for the unmanaged code that you are calling, you should subject it to a thorough code review, paying particular attention to parameter handling to make sure that buffer overflows are not possible and that it does not use potentially dangerous APIs. Dangerous APIs include:

  • Threading functions that switch security context.
  • Access token functions, which can make changes to or disclose information about a security token.
  • Credential management functions, including functions that create tokens.
  • Crypto API functions that can decrypt and access private keys.
  • Memory management functions that can read and write memory.
  • LSA functions that can access system secrets.


Avoid exposing unmanaged types or handles to partially trusted code

Partially trusted code is not allowed access to unmanaged types or handles. If you allow partially trusted callers access to elements received from an unmanaged API, you could open yourself to a luring attack.

Check all types and handles received from unmanaged code, and trace its use through your assemblies.


Avoid exposing unmanaged types or handles to partially trusted code

If your assembly makes many calls to unmanaged code, the performance overhead associated with multiple unmanaged code permission demands can become an issue.

In this situation, you can use the SupressUnmanagedCodeSecurity attribute on the P/Invoke method declaration. This causes the full demand for the unmanaged permission to be replaced with a link demand which only occurs once at just-in-time (JIT) compilation time.

Link demands make your code vulnerable to luring attacks. To mitigate the risk, you should suppress the unmanaged code permission demand only if your assembly takes adequate precautions to ensure that it cannot be coerced by malicious code to perform unwanted operations. An example of a suitable countermeasure is if your assembly demands a custom permission that more closely reflects the operation being performed by the unmanaged code


Use SuppressUnmanagedCodeSecurity with caution

If your assembly makes many calls to unmanaged code, the performance overhead associated with multiple unmanaged code permission demands can become an issue.

In this situation, you can use the SupressUnmanagedCodeSecurity attribute on the P/Invoke method declaration. This causes the full demand for the unmanaged permission to be replaced with a link demand which only occurs once at just-in-time (JIT) compilation time.

Link demands make your code vulnerable to luring attacks. To mitigate the risk, you should suppress the unmanaged code permission demand only if your assembly takes adequate precautions to ensure that it cannot be coerced by malicious code to perform unwanted operations. An example of a suitable countermeasure is if your assembly demands a custom permission that more closely reflects the operation being performed by the unmanaged code

Using SuppressUnmanagedCodeSecurity with P/Invoke

The following code shows how to apply the SuppressUnmanagedCodeSecurity attribute to a P/Invoke method declaration.

public NativeMethods
{
 // The use of SuppressUnmanagedCodeSecurity here applies only to FormatMessage
 [DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity]
 private unsafe static extern int FormatMessage(
                                     int dwFlags, 
                                     ref IntPtr lpSource, 
                                     int dwMessageId,
                                     int dwLanguageId, 
                                     ref String lpBuffer, int nSize, 
                                     IntPtr *Arguments);
}

Using SuppressUnmanagedCodeSecurity with COM Interop

For COM interop calls, the attribute must be used at the interface level, as shown in the following example.

[SuppressUnmanagedCodeSecurity]
public interface IComInterface
{
}
 

Hold pointers in private fields

If you hold pointers to unmanaged memory, for example in IntPtr fields, make sure that you mark the fields as private. This prevents someone from attempting to manipulate the pointer value, perhaps to cause an access violation or perhaps to change or remove the pointer reference and use the pointer to get access to sensitive information.

Personal tools