Security Practices: .NET Framework 2.0 Security Practices at a Glance

 

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

patterns & practices Developer Center

patterns & practices Developer Center

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

Microsoft Corporation

December 2005

Summary

This module presents a set of consolidated practices designed to address security issues related to the .NET Framework version 2.0. The answers and recommendations presented in this module are designed to supplement the companion modules and additional guidance. The practices are organized by various categories that represent those areas where mistakes are most often made. This module includes an index of practices.

Contents

How to Use This Module
What's New in 2.0
Index of Practices
Architecture and Design
Development
Deployment
Input and Data Validation
Authorization
Exception Management
Communication Security
Event Log
Data Access
Unmanaged Code
Serialization
Obfuscation
Cryptography
Sensitive Data
Code Access Security
Code Analysis
Companion Guidance
Additional Resources

How to Use This Module

To get the most from this module:

  • Use the index to browse the practices. Scan the practices and quickly jump to a specific practice.
  • Learn the practices. Learn the key items, terms, and relationships among the various practices.
  • Use the companion guidance for further details. The referenced How To modules and guideline modules can be used to obtain further details and step-by-step instructions to help you implement solutions.

What's New in 2.0

The .NET Framework version 2.0 introduces many new security features. The most notable enhancements are:

  • Global assembly cache installation means full trust. Assemblies in the global assembly cache are now always granted full trust, regardless of the security policy for the local computer.
  • Full trust assemblies now satisfy any code access security demands. In the .NET Framework 2.0, any fully trusted assembly will satisfy any demand, including a link demand for an identity permission, such as a System.Security.Permissions.StrongNameIdentityPermission, that the assembly does not satisfy.
  • SecurityException class. The System.Security.SecurityException class has been enhanced to provide more information in the case of a failed permission.
  • DPAPI managed wrapper. In the .NET Framework version 1.1, you had to use P/Invoke to access Data Protection API (DPAPI) functions. In the .NET Framework 2.0, you can use the new System.Security.Cryptography.ProtectedData class instead. ProtectedData contains two static methods: Protect and Unprotect. Managed code requires that the new System.Security.Permissions.DataProtectionPermission be able to use DPAPI. To use DPAPI to encrypt data in memory, you can use the new ProtectedMemory class.
  • SecureString class. The new System.Security.SecureString type uses DPAPI to help ensure that sensitive data stored in string form is not exposed to memory or disk-sniffing attacks.
  • XML Encryption. The System.Security.Cryptography.Xml.EncryptedXML class can be used to help protect sensitive XML that must be stored on disk.
  • Programming ACLs. You can now use the System.Security.AccessControl namespace to program access control lists (ACLs) and access control entries (ACEs) directly from managed code.
  • Programming ActiveDirectory. You can now use the System.DirectoryServices.ActiveDirectory namespace to perform Active Directory management tasks from managed code. You can use the System.DirectoryServices namespace to access data in the directory.
  • SecurityContext class. It is now easier to write asynchronous code while retaining security context and impersonation tokens. The System.Security.SecurityContext class enables you to capture the security context of a thread, including code-access security markers (such as permit and deny) and the thread impersonation token, and restore it on another thread.
  • System.Net.Security. The .NET Framework 2.0 provides a set of managed classes in the System.Net.Security namespace to provide confidentiality, integrity, and authentication between hosts. You can use Security Support Provider Interface (SSPI) or Secure Sockets Layer (SSL) to implement both client and server-side secure channels. These classes support mutual authentication, data encryption, and data signing.
  • Remoting TCP channel. The System.Runtime.Remoting.Channels.Tcp.TcpChannel class now uses SSPI to support both encryption and authentication over remoting channels.
  • Remoting IPC Channel. The new System.Runtime.Remoting.Channels.Ipc.IpcChannel class is ideal for communication between components on the same computer. The underlying implementation uses named pipes that can be secured with ACLs.
  • Sandboxed application domains. In earlier versions of the .NET Framework, to set up a sandboxed application domain—for example, to host untrusted code—you have to create an application domain policy level, create a series of code groups, and define the permission sets to be granted to each one. In the .NET Framework 2.0, you can use a new overload of the static AppDomain.CreateDomain method to help simplify this process.
  • Security transparency. You can now mark assemblies with the System.Security.SecurityTransparentAttribute to let the common language runtime (CLR) know that your code will not perform security-sensitive code access operations, such as asserting permissions or using stack-walk modifiers to escalate privileges. If your code or any code you call attempts such operations, a security exception is generated. This is particularly useful if your code loads third-party plug-ins.
  • ReflectionOnlyLoadFrom. The new Assembly.ReflectionOnlyLoadFrom method enables you to load code purely to examine its members. The loaded code is not allowed to run.
  • .NET Framework Data Provider for SQL Server. The managed data provider for SQL Server is available in medium trust. This allows partial trust applications, including ASP.NET applications, to access computers running Microsoft SQL Server.
  • .NET Framework Data Provider for OLE DB. The OLE DB managed provider no longer requires full trust. You just need the System.Data.OleDb.OleDbPermission. This allows partial trust applications to access databases that do not use SQL Server. This permission is not available to ASP.NET Web applications configured to run at medium trust. ASP.NET developers need to customize the security policy they want to use by adding the OleDbPermission permission.
  • SMTP. The System.Net.Mail.SmtpPermission permission is now available in medium trust. This allows partial trust applications to send e-mail.

Index of Practices

Architecture and Design

  • How to identify and evaluate threats
  • How to create designs that meet your security objectives
  • How to perform a security architecture and design review

Development

  • How to write secure managed code
  • How to review code for security issues

Deployment

  • How to deploy applications securely
  • How to deploy your application to run under a restricted account
  • How to protect forms authentication and authorization during deployment
  • How to sign an assembly with an X.509 certificate
  • How to choose between .snk files and .pfx files to sign an assembly with a strong name
  • How to delay-sign an assembly with a strong name
  • How to sign an assembly with Authenticode

Input and Data Validation

  • How to constrain file I/O
  • How to constrain input for length, range, format, and type
  • How to determine trust boundaries
  • How to create an input and data validation architecture

Authorization

  • How to program ACLs

Exception Management

  • How to handle exceptions securely
  • How to use structured exception handling

Communication Security

  • How to protect communication between desktop and server
  • How to protect communication between server and server
  • How to protect remoting

Event Log

  • How to set up permissions to write to the event log
  • How to use WMI for instrumentation

Data Access

  • How to protect database connection strings
  • How to use Windows authentication to connect to a SQL Server database
  • How to use SQL authentication to connect to a SQL Server database
  • How to prevent SQL injection attacks

Unmanaged Code

  • How to protect calls to unmanaged code
  • How to isolate and name unmanaged APIs
  • How to inspect for dangerous unmanaged APIs
  • How to use the safe CRT libraries

Serialization

  • How to restrict who can serialize your objects

Obfuscation

  • How to obfuscate your assembly
  • How to obfuscate your assembly with a build script

Cryptography

  • How to choose the right algorithm
  • How to choose the appropriate key size
  • How to generate a random number
  • How to manage keys and other sensitive data
  • How to use hashing
  • How to implement an integrity check
  • How to use a digital certificate
  • How to use passwords to generate keys

Sensitive Data

  • How to encrypt configuration data in configuration files
  • How to protect passwords
  • How to protect secrets in memory
  • How to use SecureString to protect data in memory
  • How to use DPAPI to protect sensitive data
  • How to use crypto algorithms to encrypt data
  • How to use XML encryption to protect sensitive data
  • How to use X.509 certificates to encrypt XML data
  • How to use XML signatures

Code Access Security

  • How to use the AllowPartiallyTrustedCallersAttribute attribute (APTCA)
  • How to use code access security
  • How to use Demand and Assert
  • How to use security transparency
  • How to use declarative and imperative permission requests
  • How to test code in a partial trust environment
  • How to use PermCalc to calculate permissions
  • How to disable code access security while debugging a partially trusted application
  • How to modify and deploy enterprise policy
  • How to apply security policy to dynamically created assemblies
  • How to use separate application domains to isolate untrusted code

Code Analysis

  • How to use FxCop

Architecture and Design

Early in the architecture and design stage of development, you should start the threat modeling activity to identify threats and vulnerabilities relevant to your application. You should also use threat modeling to help influence the key engineering decisions that you need to make during design.

  • How to identify and evaluate threats

    Rather than applying security in a haphazard manner, use threat modeling to identify threats systematically.

    To begin identifying threats, identify your security objectives. Clear objectives help you to focus the threat modeling activity and determine how much effort to spend on subsequent steps. Next, create an application overview by itemizing your application's important characteristics and actors. This helps you to identify relevant threats. After creating the overview, decompose your application. A detailed understanding of the mechanics of your application makes it easier for you to uncover more relevant and more detailed security threats. To identify threats that are relevant to your application scenario, use the details discovered so far and use a combination of threat pick lists and a question driven approach that considers the goals of a potential attacker. Finally, identify vulnerabilities by reviewing the layers of your application to find weaknesses related to the threats you already identified. Use vulnerability categories to help you focus on those areas where mistakes are most often made.

    For more information about threat modeling, see Threat Modeling Web Applications.

  • How to create designs that meet your security objectives

    Use tried and tested design patterns and principles. Focus on those areas that are critical from a security perspective, such as authentication, authorization, input / data validation, exception management, sensitive data, auditing and logging, configuration management, cryptography, and session management. Also pay attention to deployment issues at design time. Give consideration to deployment topologies, network infrastructure, security policies, and procedures.

    For more information, see Design Guidelines for Secure Web Applications.

  • How to perform a security architecture and design review

    To perform security-focused architecture and design reviews, you need to consider three major elements:

    • Deployment and infrastructure. Review the design of your application as it relates to the target deployment environment and the associated security policies. Consider the constraints imposed by the underlying infrastructure-layer security and the operational practices in use.
    • Security frame. Review the security approach that was used for the critical areas of your application. An effective way to do this is to focus on the set of categories that have the most impact on security, particularly at an architectural and design level, and where mistakes are most often made. These include authentication, authorization, input validation, exception management, and other areas. Use a patterns-based security frame as a roadmap, which will enable you to perform reviews consistently and to make sure that you do not miss any important areas during the review.
    • Layer-by-layer analysis. Review the logical layers of your application, and evaluate your security choices in your presentation, business, and data-access logic.

    For more information, see Security Architecture and Design Review.

Development

During development, you should perform regular security code-reviews to identify any security issues before your code reaches the test team. Also, you must be aware of secure coding guidelines.

  • How to write secure managed code

    To write managed code with fewer security issues, you should adhere to solid, object-oriented design principles and apply secure coding guidelines. For example, assume that all input is malicious, and validate input from all sources. For Web applications, this includes server controls, HTML controls, cookies, query strings, network resources and shared databases. Also, do not echo untrusted input back to the client. Use role-based authorization to provide access controls on public classes and class members. Use structured exception handling to return generic errors to the client. Restrict type and member visibility to limit the code that is publicly accessible. Do not store sensitive data, such as connection strings, encryption keys, and user account credentials in code.

    For more secure coding guidelines, see Security Guidelines for ASP.NET 2.0.

  • How to review code for security issues

    A properly conducted code review can increase the security of your application more than nearly any other activity. To perform a security code review, start by identifying your security code-review objectives to establish goals and constrains for the review. Then perform a preliminary scan by using a static analysis tool, such as FxCop, to find an initial set of bugs and to improve your understanding of where security issues are most likely to be discovered during further review. Next, review the most vulnerable areas of your code thoroughly to find security issues that are common to many applications. Finally, review for security issues that are unique to the architecture of your application.

    For more information, see patterns & practices Security Code Review Index.

Deployment

You should use security deployment reviews to ensure that weak or invalid configuration settings do not expose security vulnerabilities. You also need to consider how to sign your code prior to deployment.

  • How to deploy applications securely

    When you deploy your application, perform a security deployment review to identify potential security vulnerabilities introduced by inappropriate configuration settings. Make sure that your connection strings are encrypted: use protected configuration. Review any other configuration file element that could contain sensitive data, such as account credentials, and ensure that the section is encrypted if it contains sensitive data. Make sure that trace and debug are disabled. If your application accesses a database, make sure that it uses a login with limited permissions in the database. If your application uses the ActiveDirectoryMembershipProvider class, encrypt the configuration section if it contains connection credentials.

    For more information, see How To: Perform a Security Deployment Review for ASP.NET 2.0.

  • How to deploy your application to run under a restricted account

    When deploying an ASP.NET application, if your application runs on the Microsoft Windows Server 2003 operating system, make sure that the application pool that you use is running under a limited account with restricted access to the file system and registry. To use a restricted custom account, you must apply the permissions that the account requires by running the Aspnet_regiis.exe utility with the -ga switch. Afterward, configure your application to run in a dedicated application pool that runs under the identity of the custom account.

    For more information, see How To: Create a Service Account for an ASP.NET 2.0 Application.

  • How to protect forms authentication and authorization during deployment

    If your application uses forms authentication, make sure that your forms authentication is protected by appropriate configuration of the forms and machineKey elements. Ensure that authentication and role cookies are encrypted and checked for integrity. Make sure that the authorization element is configured appropriately to restrict access to your application's sensitive pages. Configure file upload limits in the maxRequestLength attribute on the httpRuntime element, and make sure that you prevent the download of unused file types by removing unnecessary MIME type mappings from IIS and by mapping unused ASP.NET file extensions to the HttpForbiddenHandler class in the httpHandlers section. Use health monitoring to log security-related events, such as failed login attempts.

    For more information, see How To: Perform a Security Deployment Review for ASP.NET 2.0.

  • How to sign an assembly with an X.509 certificate

    If you have an X.509 certificate and its private key, you can sign an assembly with the private key and a software publisher certificate generated from the X.509 certificate. You use the Signcode.exe tool to sign the assembly. You can generate an X.509 certificate for testing purposes by using the Makecert.exe tool.

    Run the Makecert.exe tool with the following switches, which creates the test X.509 certificate and its respective private key:

    makecert c:\TestCert.cer -sv c:\TestPrivateKey.pvk

    When prompted, specify a password. This is required to generate the key pair. Next, create a software publisher certificate from the X.509 certificate, by running the Cert2spc tool as shown here:

    cert2spc c:\TestCert.cer c:\TestSPCFile.spc

    Finally, use the generated software publisher certificate and the private key of the X.509 certificate to sign your assembly. To do this, use the .NET Framework Signcode tool by running the following syntax:

    signcode /spc c:\TestSPCFile.spc /v c:\TestPrivateKey.pvk <assembly tosign>

  • How to choose between .snk files and .pfx files sign an assembly with a strong name

    If you do not use delay-signing, use password-protected .pfx files to help protect your private key. Visual Studio 2005 adds support for .pfx files, which makes this very convenient. This approach is more appropriate for small- to medium-sized projects. If you previously shared or had access to .snk files that included a private key, you should use .pfx files instead (unless you use delay signing).

    Note   The AssemblyKeyFileAttribute has been deprecated in Visual Studio 2005. You should use project properties or /keyfile instead.

  • How to delay-sign an assembly with a strong name

    By using delay signing, you can restrict access to your company's private key. Members of the development team only require access to your company's public key. Use .snk files if you need to delay sign your assemblies.

    1. Create a key pair for your organization with the following command:

    sn.exe -k keypair.snk

    1. Use the following command to extract the public key from the key pair file:

    sn -p keypair.snk publickey.snk

    Secure Keypair.snk, which contains both the private and public keys. For example, put it on a compact disc, and physically secure it. Then make Publickey.snk available to all developers. For example, put it on a network share.

    To delay sign the assembly, click the Signing tab from the project properties page in Visual Studio, and then select the Sign the assembly and Delay sign only check boxes. Select your strong-name key file and then build your assembly.

    Note   A delay-signed project will not run and cannot be debugged. You can, however, use the Strong Name Tool (Sn.exe) with the -Vr option to skip verification during development.

    The delay signing process and the absence of an assembly signature mean that the assembly will fail verification at load time. To work around this, to disable verification for a specific assembly, use the following command:

    sn -Vr assembly.dll

    To disable verification for all assemblies that have a particular public key, use the following command:

    sn -Vr *,publickeytoken

    To extract the public key and key token (a truncated hash of the public key), use the following command:

    sn -Tp assembly.dll

    Note   Use an uppercase -T switch.

    To fully complete the signing process and create a digital signature to restrict access to the assembly, use the following command. This requires the private key, and as a result, the operation is normally performed as part of the formal build and release process. The following command re-signs an assembly called Assembly.dll with a strong name using the Keypair.snk key file.

    sn -R assembly.dll keypair.snk

  • How to sign an assembly with Authenticode

    To sign an assembly with an Authenticode signature, use the Signcode.exe tool. This tool is available in the .NET Framework at the following location:

    \Program Files\Microsoft Visual Studio 8\Common7\Tools\Deployment\Vspkgs

    This tool signs a portable executable (PE) file (a .dll or .exe file) with an Authenticode digital signature. You can sign either an assembly or an individual file contained in a multi-file assembly. If you are distributing an assembly, you should sign the assembly rather than the individual files.

    The following command shows how to sign an assembly:

    signcode -spc myCertificate.spc -v myKey.pvk myAssembly

    To sign with a software publisher certificate (SPC) file, you must specify the -spc and -v options if your private key is in a .pvk file. You must specify the -spc and -k options if your private key is in a registry key container.

    You can obtain a valid SPC from a certification authority, such as VeriSign or Thawte. For more information about Authenticode, see File Signing Tool (Signcode.exe).

Input and Data Validation

Proper input validation is one of your strongest measures of defense against today's application attacks. Proper input validation is an effective countermeasure that can help prevent cross-site scripting, SQL injection, buffer overflows, and other input attacks.

  • How to constrain file I/O

    If you need to apply constraints to determine which files or directories an application is able to access, you can use code access security and grant your code a limited permission using the FileIOPermission attribute. If your assembly is strong-named or contains other cryptographically strong evidence, an administrator can configure enterprise or computer-wide code access security policy to grant a restricted FileIOPermission to your assembly.

    You can configure your ASP.NET Web application to run in medium trust to limit file access to the application's virtual directory hierarchy. For more information, see How To: Use Medium Trust in ASP.NET 2.0.

  • How to constrain input for length, range, format, and type

    When input is from a server, you can use validation controls, such as RangeValidator, CustomValidator, and RegularExpressionValidator, to constrain the input by length, range, format, and type.

    When input carries an ID from any other source (such as HTML controls, query string, cookies, HTTP headers, and so on), use the System.Text.RegularExpression.Regex class to constrain input for length, range, format, and type.

    For more information about using regular expressions, see How To: Use Regular Expressions to Constrain Input in ASP.NET.

  • How to determine trust boundaries

    To help identify trust boundaries, start by identifying your outer system boundaries. For example, your application can write to files on server x, it can make calls to the database on server y, and it can call Web service z. This defines your system boundary. Next, identify access control points, or the key places where access requires additional privileges or role membership. For example, a particular page might be restricted to managers. The page requires authenticated access and requires that the caller is a member of a particular role. Finally, identify trust boundaries from a dataflow perspective.

    For each subsystem, consider whether the upstream data flow or user input is trusted, and if it is not, consider how the data flow and user input can be authenticated and authorized. Knowing which entry points exist between trust boundaries enables you to focus your threat identification on these key entry points. For example, you are likely to have to perform more validation on data passed through an entry point at a trust boundary.

    Trust boundaries indicate where trust levels change. You can think of trust from the perspective of confidentiality and integrity. For example, a change in access control levels in your application, where a specific role or privilege level is required to access a resource or operation, would be a change in a trust boundary. Another example would be at an entry point in your application where you might not fully trust the data passed to the entry point.

  • How to create an input and data validation architecture

    When you develop an input- and data-validation architecture for your application, consider developing a library of validation routines in all but the smallest applications. This will help ensure that data is validated in a consistent way throughout the application and provide a single point of maintenance. You need to trace data from entry point to exit point to know how it should be validated. A good library includes routines for all of the different types of validation you need to apply, and these can be used in combination if necessary. For example, you should have routines to apply validation for SQL injection and other routines to apply validation for cross-site scripting.

    Constrain what you allow from the beginning. It is much easier to validate data for known valid types, patterns, and ranges (using a safe list) than it is to validate data by looking for known bad characters (using a blocked list). When you design your application, you know what your application expects. The range of valid data is generally a more finite set than the range of potentially malicious input. However, for added defense you might want to reject known bad input and then sanitize the input. Constrain input for type, length, format, and range. Use regular expressions to help constrain text input. Use strong data typing where possible.

    Also identify your trust boundaries, and ensure that entry points between trust boundaries validate all input data explicitly. Make no assumptions about the data. The only exception is inside a routine that you know can only be called by other routines within the same trust boundary.

    For more information about protecting Web applications from injection attacks, see How To: Protect from Injection Attacks in ASP.NET.

Authorization

Authorization determines what the authenticated identity can do and which resources it can access. Improper, or weak, authorization leads to information disclosure and data tampering. Authorization is usually role-based, where users are partitioned into application-defined logical roles; or it is resource-based, where Windows ACLs are attached to resources.

  • How to program ACLs

    Use classes available in the System.Security.AccessControl namespace to program discretionary access control lists (DACLs) and system access control lists (SACLs) for Windows objects.

    You can use the Security, AccessRule, and AuditRule classes (which are available for each of the system objects listed earlier) to modify access control programmatically, to create new objects by copying ACLs from one object to another, or to set security audits on various system objects.

    The following code example shows how to add an ACL to a file.

    using System;
    using System.Security.AccessControl;
    using System.Security.Principal;
    using System.IO;
    
    // Create an empty Security Descpriptor.
    FileSecurity newFileSec = new FileSecurity();
    
    // Give Read permission to user on this file.
    newFileSec.AddAccessRule(
        new FileSystemAccessRule(@"Domain\UserName",
            FileSystemRights.Read,AccessControlType.Allow)
    );
    
    // Set a security audit rule for read operations. 
    // Note - An audit rule for a user account requires a 
    // corresponding access rule for the same user account.
    // To permit file and directory auditing on Microsoft Windows NT
    // or later, you must enable Audit Access Security policy on
    // your computer. By default, this policy is set to No Auditing.
    newFileSec.AddAuditRule(
        new FileSystemAuditRule(@"Domain\UserName",
            FileSystemRights.Read, AuditFlags.Success)
    );
    
    // Create a file with the permissions and audit rule.
    FileInfo newFile = new FileInfo("e:\\test1.txt");
    newFile.Create();
    
    // Set the access control on the newly created file.
    newFile.SetAccessControl(newFileSec);
    
    

    For more information, see New Ways to Manage Active Directory using the .NET Framework 2.0.

Exception Management

Robust exception-handling code is essential to help ensure application stability and to help prevent sensitive exception details, which could be useful to an attacker, from reaching the client.

  • How to handle exceptions securely

    If your application throws an exception, make sure that it fails without disclosing sensitive information, denies access, and is not left in an unsecured state. Do not log sensitive or private data, such as passwords, that could be compromised.

    Do not reveal internal system or application details, such as stack traces, SQL statements, and table or database names. Make sure that this type of information is not allowed to propagate to users of your application or beyond your current trust boundary. A malicious user could use system-level diagnostic information to learn about your application and probe for weaknesses to exploit in the future.

  • How to use structured exception handling

    Use structured exception-handling to avoid unhandled exceptions. Use finally blocks to ensure that resources are released, files are closed, and objects are disposed of—regardless of whether an exception occurs. The following code example shows to use a finally block to ensure that a database connection is closed promptly.

    using System.Data.SqlClient;
    using System.Security;
    
    SqlConnection conn = new SqlConnection("...");
    try
    {
        conn.Open();
        // Do some operation that might cause an exception.
    
        // Calling Close as early as possible.
        conn.Close();
        // ... other potentially long operations.
    }
    finally
    {
        if (conn.State==ConnectionState.Open)
            conn.Close();  // ensure that the connection is closed.
    }
    
    

    To help diagnose security issues, consider adding a catch block for the SecurityException class. SecurityException has been enhanced to provide more information in case of a failed permission demand. Additionally, to diagnose role-based access or identity problems, consider logging the identity at the time of the exception, as shown in the following code example.

    catch(Exception ex)
    {
    ...
        // Log current identity together with other exception information.
        WindowsIdentity wid = WindowsIdentity.GetCurrent();
    ...
    }
    
    

Communication Security

If you transmit data (such as credentials or sensitive application-specific data) over the network, you need to consider how to protect the communication channel. You need to ensure that sensitive data cannot be viewed or modified without detection.

  • How to protect communication between desktop and server

    Use SSL to encrypt the communication channel between specific client applications and a server. For example, you could use SSL to encrypt the channel between a desktop application and a remote computer running SQL Server.

  • How to protect communication between server and server

    Use Internet Protocol security (IPsec) to encrypt the communication channel between two servers and to restrict which computers can communicate with one another. For example, you can help secure a database server by establishing a policy that permits requests only from a trusted client computer, such as an application or Web server. You can also restrict communication to specific IP protocols and TCP/UDP ports.

    Use SSL instead of IPSec when you need detailed channel protection for a particular application instead of for all applications and services running on a computer.

  • How to protect remoting

    You can use new security-related functionality in the System.Net.Security namespace to help secure .NET remoting applications. You can apply security programmatically or more often through configuration-file options. Remoting security uses the NegotiateStream class. This provides a stream that uses the Negotiate security protocol to authenticate the client, and optionally the server, in client-server communication. The following code example shows how to configure a remoting server.

    <configuration>
      <system.runtime.remoting>
        <application>
          <service>
            <wellknown mode="Singleton"
              type="RemotingSample.RemObject, RemotingSample"
              objectUri="RemObjectEndpoint.rem" />
          </service>
          <channels>
            <channel ref="tcp" secure="true" port="8080" />
          </channels>
        </application>
       </system.runtime.remoting>
    </configuration>
    
    

    The channel element in the preceding configuration specifies an encrypted TCP communication channel that uses port 8080. By setting the secure attribute of the channel element to true, you help protect the communication channel. The protectionLevel attribute has not been specified, so the default setting of EncryptAndSign is used. This causes transmitted data to be encrypted and signed.

    The following code example shows a client configuration for protected remoting.

    <configuration>
      <system.runtime.remoting>
        <application>
          <channels>
            <channel ref="tcp" secure="true" useDefaultCredentials="true"/>
          </channels>
        </application>
      </system.runtime.remoting>
    </configuration>
    
    

    The client configuration must also specify a protected channel by setting secure="true". Because the tokenImpersonationLevel attribute has not been specified in the client configuration, it defaults to Identification. This allows the remoting server to obtain security information about the client (such as security identifiers and privileges) without giving the remoting server the ability to impersonate the client.

    Note   A secure TCP-client channel can connect only to secure TCP-server channel and an unsecured TCP-client channel may connect only to a non-secure TCP-server channel.

    On both the server and the client, you need to register the configuration by calling the Configure method, as shown in the following code example.

    // Add the following to your remoting server application.
    RemotingConfiguration.Configure("YourRemotingServerAppName.exe.config", true);
    
    // Add the following to your remoting client application.
    RemotingConfiguration.Configure("YourRemotingClientAppName.exe.config", true);
    
    

Event Log

Code that writes to the event log needs to be granted the EventLogPermission by code access security policy. Also, you might need to create application specific event sources.

  • How to set up permissions to write to the event log

    If your code needs to use application specific event sources, you should create them at installation time when administrator privileges are available. You do so by creating an installer class that derives from System.Configuration.Install.Installer and using the InstallUtil.exe utility. If you are unable to create event sources at installation time, and you are in deployment, the administrator can manually create new event source entry beneath the following registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\<LogName>

    Note   You should not grant write permission to the ASP.NET process account (or any impersonated account if your application uses impersonation) on the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\ registry key. If you allow write access to this key and the account is compromised, the attacker can modify any log-related setting, including access control to the log, for any log on the system.

    To access the event log, your assembly must be granted EventLogPermission by code-access security policy. In partial trust environments, if you want to constrain the actions of event-log wrapper code—perhaps because the code was written by another developer or development organization—you can use declarative attributes with the SecurityAction.PermitOnly enumeration as shown in the following example.

    The following attribute ensures that the WriteToLog method and any methods it calls can only access the local computer's event log and cannot delete event logs or event sources. These operations are not permitted by the EventLogPermissionAccess.Instrument enumeration.

    using System.Diagnostics;
    
    ...
     [EventLogPermission(SecurityAction.PermitOnly, 
                        MachineName=".",
                        PermissionAccess=EventLogPermissionAccess.Instrument)]
    public static void WriteToLog( string message )
    
    

    To enforce read-only access to existing logs, use the EventLogPermissionAccess.Browse enumeration.

    For more information about creating event sources at installation time, see Installer Tool (Installutil.exe).

  • How to use WMI for instrumentation

    The System.Management assembly defines classes that you can use to instrument your application with Windows Management Instrumentation (WMI). To instrument specific aspects of your .NET Framework application, you need to define and register with the Common Information Model Object Manager (CIMOM) a WMI-managed class that captures required information when that specific event occurs in your application. You need to call the Fire method of the WMI-managed event class to notify the WMI infrastructure and monitoring applications that the event has occurred.

    To use WMI, create a WMI-managed class that derives from the BaseEvent class and register the schema with CIMOM. Add the Instrumented assembly-level attribute to the assembly that contains the WMI-managed class definition as shown here.

    using System;

    using System.ComponentModel;

    using System.Management.Instrumentation;

    ...

    System.Management.Instrumentation;[assembly:Instrumented(@"root\MyAppWMI")]

    namespace MyApp.WMI
    {
        [ManagedName("MyApp_MyAppExceptionEvent")]
        public class MyAppExceptionEvent : BaseEvent
        ...
    }
    
    

    Add any event-specific properties (to the class) that you would like to be captured when monitoring the application.

    To register the schema of your WMI-managed class with CIMOM, create a new class that derives from the DefaultManagementProjectInstaller class, as shown in the following code example.

    namespace MyApp.WMI
    {
        [RunInstaller(true)]
        public class MyAppWmiSchemaInstaller : 
                        DefaultManagementProjectInstaller 
    }
    
    
    1. To register the class, run the InstallUtil.exe tool, as follows:

    installutil MyApp.Wmi.dll

    At runtime, to instrument an event, call the Fire method of the WMI-managed class when the specific event occurs in the application as shown here.

    try
    {
    }
    catch(MyAppException ex)
    {
        new MyAppExceptionEvent().Fire();
    }
    
    

    For more information about WMI in the .NET Framework, see Managing Applications Using WMI.

Data Access

The main issues to consider while developing data-access code include how to protect connection strings, how to authenticate with the database, and how to prevent SQL injection attacks.

  • How to protect database connection strings

    To protect database connection strings, store them inside the connectionStrings section in your configuration file and then use protected configuration to encrypt this section. For more information, see the section, How to Encrypt Configuration Data in Configuration Files, later in this document.

  • How to use Windows authentication to connect to a SQL Server database

    To use Windows authentication, configure a SQL Server database for Windows authentication, and then use a connection string that contains either "Trusted_Connection=Yes" or "Integrated Security=SSPI", as shown in the following code example. The two strings are equivalent and both result in Windows authentication.

    "server=MySQL; Integrated Security=SSPI; database=Northwind"
    "server=MySQL; Trusted_Connection=Yes; database=Northwind"
    
    

    For more information, see How To: Connect to SQL Server Using Windows Authentication in ASP.NET 2.0.

  • How to use SQL authentication to connect to a SQL Server database

    If you cannot use Windows authentication to connect to a SQL Server database, you must use SQL authentication. When you use SQL authentication, make sure that you use a least-privileged user ID to connect to a SQL Server database and do not use highly privileged accounts such as the sa account. Use a strong password for the SQL user account and protect the channel between your application and database server because credentials are passed in an unencrypted format. For example, use SSL or IPSec. Finally, encrypt the connection string because SQL connection strings contain plaintext credentials.

    For more information, see How To: Connect to SQL Server Using SQL Authentication in ASP.NET 2.0.

  • How to prevent SQL injection attacks

    To help prevent SQL injection attacks, start by thoroughly validating and constraining input. Validate input for type, length, format, and range. Use regular expressions to validate text input. Also use parameterized stored procedures for data access. This ensures that input values are checked for type and length. Parameters are also treated as safe literal values and not as executable code within the database. Avoid stored procedures that accept a single parameter that has the query to execute. Instead, pass query parameters only. If you cannot use stored procedures, use SQL statements with parameters and do not build SQL statements by concatenating input values with SQL commands.

    Use structured exception handling to catch exceptions when your code accesses the database, and make sure that you do not return exception details to the client. As an additional precaution, make sure that the account your application uses to connect to the database has restricted permissions in the database.

    For more information about preventing SQL injection attacks, see How To: Protect From SQL Injection in ASP.NET.

Unmanaged Code

If your application calls unmanaged code, you need to consider the threat of buffer overflow attacks. Also, because unmanaged code is not subject to code access security and can perform any operation that is subject to operating-system security, you must carefully review all calls to unmanaged code to help ensure that the code cannot be exploited.

  • How to protect calls to unmanaged code

    Restrict access to unmanaged code by demanding custom permissions before asserting the unmanaged code permission. When you call unmanaged code, validate input that is passed to unmanaged APIs and guard against potential buffer overflows and array boundary violations. Validate the lengths of input and output string parameters, validate array bounds, and check file path lengths. Inspect unmanaged code for unsafe APIs.

    If you own the unmanaged code, make sure that you compile it with the -GS switch to enable 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.

  • How to isolate and name unmanaged APIs

    Isolate all unmanaged API calls in a wrapper assembly. This makes it easier to review your code that calls unmanaged code, and it enables you to easily determine the set of unmanaged APIs on which your application depends. You can also isolate the unmanaged code permission to a single assembly.

    Use naming conventions to keep native code risks in mind when you develop and review code. Categorize your unmanaged code and use a prefix to indicate the types that you use to encapsulate the unmanaged APIs.

    Use the word Safe to identify code that poses no possible security threat and is harmless for any code, malicious or otherwise, to call. You can annotate safe classes with the SuppressUnmanagedCodeSecurity attribute, which turns off the code-access security permission's demand for full trust.

     [SuppressUnmanagedCodeSecurity]
    class SafeNativeMethods {
        DllImport("user32")]
        internal static extern void MessageBox(string text);
    }
    
    

    Use the word Native to identify unmanaged code that is potentially unsafe but 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 the SuppressUnmanagedCodeSecurity attribute.

    class NativeMethods {
        [DllImport("user32")]
        internal static extern void FormatDrive(string driveLetter);
    }
    
    

    Use the word Unsafe to identify unmanaged code that is potentially unsafe and that has the security demand for the unmanaged code permission declaratively suppressed. Any caller of these methods must do a full security review to ensure that the usage is safe because no stack walk is performed.

     [SuppressUnmanagedCodeSecurity]
    class UnsafeNativeMethods {
        [DllImport("user32")]
        internal static extern void CreateFile(string fileName);
    }
    
    
  • How to inspect for dangerous unmanaged APIs

    You should be able to justify the use of all Win32 API calls. Unsafe APIs include threading functions that switch security context, such as CreateThread, RevertToSelf and access token functions, which can create tokens and can modify or disclose information about a security token. These include AdjustTokenGroups, AdjustTokenPrivileges, SetTokenInformation, and GetTokenInformation. Other unsafe APIs include credential management functions, including functions that create tokens, such as LogonUser and LogonUserEx, crypto API functions that can decrypt and access private keys, memory management functions that can read and write to memory and Local Security Authority (LSA) functions that can access sensitive system data.

  • How to use the safe CRT libraries

    The safe C Runtime (CRT) libraries are updated versions of the standard C and C++ libraries, including the CRT Library, Standard C++ Library (SCL), Active Template Library (ATL), and Microsoft Foundation Classes (MFC). The updates are designed to help protect applications compiled with Visual C++. They add appropriate buffer checks to functions that are known to be vulnerable to attack; they validate parameters; and they deprecate other functions, such as strcpy, which are known to be vulnerable to attack.

    Use the secure version of a function if it exists. If a new secure function exists, the older, less secure version is marked as deprecated and the new version has the _s (secure) suffix. For example, use strcpy_s instead of strcpy. Note that the compiler will generate a warning if you use a deprecated function.

    For a list of secure CRT functions, see Security-Enhanced Versions of CRT Functions.

Serialization

You might need to add serialization support to a class if you need to be able to marshal it by value across a .NET remoting boundary (that is, across application domains, processes, or computers), or if you want to be able to persist the object state to create a flat data stream, perhaps for storage on the file system.

  • How to restrict who can serialize your objects

    If you create a class that implements the ISerializable interface, which allows your object to be serialized, you can add a permission demand to your ISerializable.GetObjectData implementation to authorize the code that is attempting to serialize your object. This is particularly important if your code supports partial trust callers.

    For example, the following code fragment uses a System.Security.Permissions.StrongNameIdentityPermission attribute to help ensure that only code signed with a particular private key, which corresponds to the public key in the demand, can serialize the object's state.

    using System.Diagnostics;
    ...
    [StrongNameIdentityPermission(SecurityAction.Demand,
                                  PublicKey="00240000048...97e85d098615")]
    public override void GetObjectData(SerializationInfo info, 
                                       StreamingContext context)
    
    

    Note   In the .NET Framework 2.0, any fully trusted assembly will satisfy any demand, including a link demand for an identity permission, such as System.Security.Permissions.StrongNameIdentityPermission which the assembly does not satisfy.

Obfuscation

If you are concerned with protecting intellectual property, you can make it extremely difficult for a decompiler to be used on the Microsoft intermediate language (MSIL) code of your assemblies by using an obfuscation tool. An obfuscation tool confuses human interpretation of the MSIL instructions and helps prevent successful decompilation. This threat is particularly significant for assemblies that are distributed to end users or installed on end user computers. It is generally less of a concern for assemblies on Web servers.

Note   Obfuscation is not foolproof and you should not build security solutions that rely on it. However, obfuscation does address threats that stem from the ability to reverse engineer code.

  • How to obfuscate your assembly

    To obfuscate your assemblies you can use Dotfuscator Community Edition which is included with Visual Studio. 2005. Ensure that you have a mechanism to deal with exception messages and stack traces from the obfuscated assembly because these are also obfuscated. You can use the map file generated by Dotfuscator which has the mapping between the old and the new symbol names. You can also use the Lucidator tool that is supplied with the standard and professional versions of Dotfuscator for this purpose.

    Use declarative obfuscation to specify obfuscation parameters in code for more fine-grained control. The .NET Framework version 2.0 has two new custom attributes, which aid Dotfuscator while obfuscating assemblies: System.Reflection.ObfuscateAssemblyAttribute and System.Reflection.ObfuscationAttribute. Use System.Reflection.ObfuscateAssemblyAttribute to specify assembly-level obfuscation attributes and System.Reflection.ObfuscationAttribute to specify member-level obfuscation settings.

    Use the member-level attribute to control which members should be obfuscated. Make sure that you obfuscate shared assemblies by specifying library mode to ensure that public entry points are not obfuscated. You can do this either by using ObfuscateAssemblyAttribute or by setting the appropriate configuration in the Dotfuscator tool.

    For more information, see .NET Obfuscator - Dotfuscator Team Page.

  • How to obfuscate your assembly with a build script

    If you need to create obfuscated assemblies on your build server from a script, use the Dotfuscator command-line interface. To execute Dotfuscator.exe from the command line either add the Dotfuscator executable path to the path environment variable or browse to it under the Application\PreEmptive Solutions\Dotfuscator folder beneath your VS installation.

    By default, running Dotfuscator from the command prompt opens the standalone graphical user interface (GUI). To open the command-line interface, use the -v (verbose) switch. You can use the command-line tool to specify a previously created configuration file, use command-line switches to perform the obfuscation directly or use a combination of the configuration file and command-line switches, where the switch options can override or supplement settings in the configuration file.

    The following example obfuscates both My1.dll and My2.dll in verbose mode:

    dotfuscator /v /in My1.dll,My2.dll

    The output assemblies are written to a directory called .\Dotfuscated, and the map file is written to .\Dotfuscated\map.xml because no output directories are specified.

    For more information, see .NET Obfuscator - Dotfuscator Team Page.

Cryptography

Cryptography is one of the most important tools that you can use to help protect data. Encryption can be used to provide data privacy and hash algorithms, which produce a fixed and condensed representation of data. Also, digital signatures can be used for authentication purposes.

You should use encryption when you want data to be secure in transit or in storage. Some encryption algorithms perform better than others, while some provide stronger encryption. Typically, larger encryption key sizes increase security.

  • How to choose the right algorithm

    You choose an algorithm based on your scenario and requirement. For example, if you require data confidentiality use an encryption algorithm. If you require data integrity use a hash-based message authentication code or a digital signature. You use hashing when you want to avoid storing a password and instead compare the hash value with a recalculated hash obtained from the user supplied password. 

    Symmetric and asymmetric encryption are often used together. A symmetric encryption key is used as a session key to encrypt message data, and asymmetric encryption is used to encrypt the symmetric key and initialization vector prior to exchanging these values between two parties.

    Classes in the System.Security.Cryptography namespace provide support for symmetric encryption, asymmetric encryption, hashing, generating random numbers, and creating digital signatures. Use the following guidelines when you select a cryptographic algorithm:

    • Symmetric encryption. Use these to encrypt large streams of data. Use Rijndael (now referred to as Advanced Encryption Standard [AES]) or Triple Data Encryption Standard (3DES).
    • Asymmetric or public/private key encryption. The .NET Framework provides managed classes for Rivest, Shamir, Adleman (RSA), and Digital Signature Algorithm (DSA) asymmetric encryption. DSA can be used for digital signatures only. If interoperability is an issue, you need to find out what algorithms the other party you are interoperating with supports, and use those. If you are interested in both signing and encryption, select RSA. If you are concerned about performance, you should be aware that DSA is faster at key generation and RSA is faster at signature generation. Measure the performance of both approaches in your scenario.
    • Hashing. Use a hashing algorithm such as SHA256 when you need a principal to prove it knows a secret that it shares with you. The main drawback with using hashes for passwords is that they do not allow you to retrieve the password later, if needed. Use HMACSHA256 with Message Authentication Codes (MAC), which require you and the client to share a key. This can provide integrity checking and a degree of authentication.
  • How to choose the appropriate key size

    When generating an encryption key or key pair, use the largest key size that the algorithm supports. This does not necessarily make the algorithm more secure, but it dramatically increases the time needed to successfully perform a brute force attack on the key. The following code example shows how to find the largest supported key size for a particular algorithm.

    private int GetLargestSymKeySize(SymmetricAlgorithm symAlg)
    {
        KeySizes[] sizes = symAlg.LegalKeySizes;
        return sizes[sizes.Length - 1].MaxSize;
    }
    private int GetLargestAsymKeySize(AsymmetricAlgorithm asymAlg)
    {
        KeySizes[] sizes = asymAlg.LegalKeySizes;
        return sizes[sizes.Length - 1].MaxSize;
    }
    
    
  • How to generate a random number

    If you need to generate encryption keys programmatically, use the RNGCryptoServiceProvider class to create keys and initialization vectors and do not use the Random class. Unlike the Random class, RNGCryptoServiceProvider creates cryptographically strong random numbers that are FIPS-140 compliant. The following code example shows how to use this function.

    using System.Security.Cryptography;
    . . .
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] key = new byte[keySize];
    rng.GetBytes(key);
    
    
  • How to manage keys and other sensitive data

    To avoid key management, consider using DPAPI encryption where possible. With DPAPI, the operating system manages the key. For example, a good use for DPAPI is to encrypt symmetric encryption keys used to encrypt data in a database.

    Cycle keys periodically. Change your encryption keys regularly because a static secret is more likely to be discovered over time. Do not overuse keys.

    Protect exported private keys. Use the PasswordDeriveBytes class when you export an RSA or DSA private key. The RSA and DSA classes contain a ToXmlString method, which enables you to export the public key or private key (or both) from the key container. This method exports the private key in plain text. If you export the private key to be installed on multiple servers in a Web farm, you should encrypt the key after exporting the private key by using PasswordDeriveBytes to generate a symmetric key, as shown in the following code example.

    using System.Security.Cryptography;
    ...
    PasswordDeriveBytes deriver = new PasswordDeriveBytes(<strong password>, null);
    byte[] ivZeros = new byte[8];//This is not actually used but is currently required.
    //Derive key from the password
    byte[] pbeKey = deriver.CryptDeriveKey("TripleDES", "SHA1", 192, ivZeros);
    
    
  • How to use hashing

    Use a hashing algorithm, such as SHA256, SHA1 or MD5, when you need a principal to prove it knows a secret that it shares with you. You can use SHA256 on the .NET Framework version 2.0 because it provides a managed implementation of SHA256. For .NET Framework version 1.1, you should use SHA1 and not MD5 because it generates a 160-bit hash output, while MD5 generates a 128-bit output. The following code example shows how to use a hashing algorithm, such as SHA1, to hash data.

    using System.Security.Cryptography;
    ...
    // Create a new instance of the hash crypto service provider.
    HashAlgorithm hashAlg = new SHA1CryptoServiceProvider(); 
    
    // Convert the data to hash to an array of Bytes.
    byte[] bytValue = System.Text.Encoding.UTF8.GetBytes(stringDataToHash);
    
    // Compute the Hash. This returns an array of Bytes.
    byte[] bytHash = hashAlg.ComputeHash(bytValue);
    
    // Optionally, represent the hash value as a base64-encoded string, 
    // For example, if you need to display the value or transmit it over a network.
    string base64 = Convert.ToBase64String(bytHash);
    
    

    When you have computed and stored a hash value for a password (for example), you can check that a caller knows the password by prompting for the password, using the supplied data to recalculate the hash value, and then comparing the result to the stored hash.

    Note   Password hashing enables you to verify—but not retrieve—a password value.

    If you use hashes to store passwords in a database, you should combine the password with a random salt value before computing the hash. You can compute the salt value by using the RNGCryptoServiceProvider class, as shown in the following code example.

    using System.Security.Cryptography;
    ...
    private static string CreateSalt(int size)
    {
        // Generate a cryptographic random number using the cryptographic
        // service provider
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        byte[] buff = new byte[size];
        rng.GetBytes(buff);
        // Return a Base64 string representation of the random number
        return Convert.ToBase64String(buff);
    }
    
    

    Note   If you use the ASP.NET SQL Server membership provider, you can configure it to store password hashes with added salt by setting passwordFormat="Hashed" on the provider configuration.

  • How to implement an integrity check

    You can use integrity checking to verify that data sent via an unsecured communication channel has not been modified. You can implement integrity checks on your data with a data hash or a digital signature. A data hash alone does not ensure integrity because both the data and the hash could be modified by a malicious user.

    Use a digital signature or the System.Security.Cryptography.HMACSHA1 hashing class to help ensure data integrity. An integrity check is performed by verifying that the hash value of the received data matches the hash value of the data that was sent. The Hash-based Message Authentication Code (HMAC) verifies that both the hashed data and the hash itself have not been altered.

    When you perform an integrity check with an HMAC, follow these guidelines:

    • The sender and receiver must both have knowledge of a private key that will be used to calculate the HMAC.
    • Generate an HMAC from the data by using the HMACSHA1 class.
    • Send the data and the HMAC to the recipient.
    • Verify the integrity of received data by computing an HMAC on the data and comparing it to the HMAC sent. If the private key has not been stolen and the HMAC values match, you can be sure that the data has not been modified.

    The following code example shows how to compute an HMAC:

    using System; 
    using System.IO;
    using System.Security.Cryptography; 
    public class ComputeHMAC 
    { 
        public static void Main (string[] args) 
        { 
            byte[] key = new byte[KEY_SIZE];
            byte[] data = new byte[DATA_SIZE];
    
            // Key should contain the value of the private shared key.
            HMACSHA1 hmac = new HMACSHA1(key);
            CryptoStream cs = new CryptoStream(Stream.Null, hmac,
                                               CryptoStreamMode.Write);
            cs.Write(data, 0, data.Length);
            cs.Close();
            byte[] result = hmac.Hash;
          }
    }
    
    
  • How to use a digital certificate

    Use the System.Security.Cryptography.X509Certificates namespace to access digital certificates. You can load a certificate from a file or select one from a physical store. Use the X509Certificate2 class to load a certificate from a file, and use the X509Store class to load a certificate from a store.

    The following code example shows how to load a certificate from a file and add it to the local certificate store.

    using System; 
    using System.Security.Cryptography; 
    using System.Security.Cryptography.X509Certificates; 
    
    public class X509store2 
    { 
        public static void Main (string[] args) 
        { 
            // Create new X509 store called teststore from the 
            // local certificate store. 
            X509Store store = new X509Store ("teststore", 
                                             StoreLocation.CurrentUser); 
            store.Open (OpenFlags.ReadWrite); 
            // Create certificate from a certificate file. 
            X509Certificate2 certificate = new X509Certificate2(
                                          "c:\\mycerts\\*****.cer"); 
            // Add certificate to the store. 
            store.Add(certificate); 
            // Close the store. 
            store.Close (); 
      } 
    }
    
    

    You can use certificates with the SslStream class to authenticate the server or the client during an SSL transaction. Use the SslStream.AutheticateAsServer or SslStream.AutheticateAsClient enumerations to complete the authentication process.

  • How to use passwords to generate keys

    In .NET version 1.1 you could use PasswordDeriveBytes to generate keys from a password. NET version 2.0 still supports this for backward compatibility but you should now use Rfc2898DeriveBytes. The main advantage is that it supports the RSA Password-Based Key Derivation Function version 2 (PBKDF2), which is an improved version of the PBKDF1 standard implementation used by PasswordDeriveBytes.

    To use Rfc2898DeriveBytes, supply at minimum a password and salt value. You can also specify a number of iterations to derive the key if you want to override the default 1000 iterations. Rfc2898DeriveBytes uses HMACSHA1 as an underlying pseudo-random generator. After the key is used to encrypt the data, clear it from memory but persist the salt and the number of iterations if you changed the default value. These values should be protected and are needed to regenerate the key for decryption.

    The following code shows how to call Rfc2898DeriveBytes.

    using System.Security.Cryptography;
    ...
    // Get salt (random bytes) using RNGCryptoServiceProvider.
    byte[] salt = new byte[8];
    new RNGCryptoServiceProvider().GetBytes(salt);
    // Get the Password derived bytes using password and salt.
    Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes ("P@ssword!",
                                                                salt);
    // Create a TripleDESCryptoServiceProvider object.
    TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
    
    // Get the password derived bytes key of length 16 bytes and add 
    // it to the Key property.
    tdes.Key = passwordBytes.GetBytes(16);
    // Use the TripleDESCryptoServiceProvider object to encrypt and decrypt data.
    ...
    
    

    Note   To decrypt the data you will need the initialization vector (IV) value used by the encryption routine, so that must also be available to the decryption code.

Sensitive Data

Applications that deal with private user information, such as credit card numbers, addresses, medical records, and so on should take special steps to make sure that the data remains private and unaltered. In addition, sensitive data that is used by the application's implementation, such as passwords and database connection strings, must be protected.

  • How to encrypt configuration data in configuration files

    For ASP.NET applications, if you store sensitive data in the Web.config file, you can use the ASP.NET IIS Registration Tool (Aspnet_regiis.exe) to encrypt configuration file sections by using either DPAPI or RSA encryption. This tool uses the protected configuration provider in the .NET Framework 2.0. For more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.

    Note   In Web farms, you should use RSA encryption due to the ease with which encryption keys can be exported.

    There is no equivalent tool for encrypting data in Windows application configuration files. Instead, you must use the protected configuration API, as shown in the following code example. This example encrypts the connectionStrings section of an application's App.config file by using the DPAPI protected configuration provider.

    Using System.Configuration;
    ExeConfigurationFileMap filemap = new ExeConfigurationFileMap();
    filemap.ExeConfigFilename = @"c:\Samples\SampleApp\App.config";
    
    Configuration testConfig = ConfigurationManager.OpenMappedExeConfiguration(
                                         filemap, ConfigurationUserLevel.None);
    ConfigurationSection configSection = testConfig.GetSection(
                                                    "connectionStrings");
    if (configSection.SectionInformation.IsProtected)
        configSection.SectionInformation.UnprotectSection();
    else
        // This example uses the DPAPI protected configuration provider
        configSection.SectionInformation.ProtectSection(
                                        "DataProtectionConfigurationProvider");
    testConfig.Save();
    
    
  • How to protect passwords

    Make sure that your applications enforce the use of strong passwords. If your applications use Windows authentication, you benefit from being able to use Windows or Active Directory password policy. If you need to store passwords in configuration files, make sure that you encrypt them. Use the protected configuration feature to encrypt the configuration section that stores the password. For ASP.NET applications, you can use Aspnet_regiis.exe. For other application types, such as Windows applications, you must programmatically encrypt configuration file sections by using the protected configuration API.

    If you store passwords in a database for authentication, do not store them in plaintext or encrypted form. Instead, store non-reversible hashes with added salt.

  • How to protect secrets in memory

    You should consider encrypting sensitive data, such as user names and passwords, in memory as well as in persistent stores. This helps protect the data even if someone manages to probe your computer's memory or obtain a process-crash dump file.

    You can protect sensitive data in memory by using the ProtectedMemory class. The ProtectedMemory class is a managed wrapper for DPAPI. You can also use the SecureString type for storing sensitive strings in memory. SecureString data is automatically encrypted (by using ProtectedMemory). You can also programmatically clear SecureStrings in memory.

    The following code example shows how to use the ProtectedMemory class to encrypt and decrypt data in memory.

    using System.Security.Cryptography;
    ...
    byte[] optionalEntropy = {7,5,4,9,0};
    byte[] dataToBeEncrypted = Encoding.Unicode.GetBytes("Test String 1211");
    // Encrypt the data in memory
    ProtectedMemory.Protect(dataToBeEncrypted,  
                            MemoryProtectionScope.SameLogon);
    // Decrypt the data in memory
    ProtectedMemory.Unprotect(dataToBeEncrypted, 
                              MemoryProtectionScope.SameLogon);
    string originalData = Encoding.Unicode.GetString(dataToBeEncrypted);
    
    
  • How to use SecureString to protect data in memory

    Use of the SecureString class is less appropriate in ASP.NET applications. It is unlikely that you can extract data from a Web page that contains sensitive data (such as a credit card number) and place it inside a SecureString without it having already passed through intermediate System. String objects. In some scenarios, for example in console applications, you can take sensitive data directly from the user and construct a SecureString object one character at a time by using the AppendChar method. The following code example shows how to do this.

    using System.Securtiy;
    SecureString securePassword = new SecureString(); 
    Console.WriteLine("Enter Password...."); 
    while (true) 
    {
        ConsoleKeyInfo conKeyInfo = Console.ReadKey(true);
        if (conKeyInfo.Key == ConsoleKey.Enter)
            break;
        else if (conKeyInfo.Key == ConsoleKey.Escape)
            return;
        else if (conKeyInfo.Key == ConsoleKey.Backspace)
        {
            if (securePassword.Length != 0)
                securePassword.RemoveAt(securePassword.Length - 1);
        }
        else
            securePassword.AppendChar(conKeyInfo.KeyChar);
    }
    Console.WriteLine(securePassword.Length.ToString());
    
    

    Note   SecureString does not support inspection, comparison, or conversion functionality. It cannot be manipulated to reveal the data.

    To subsequently use the data from SecureString, use the Marshal.SecureStringToBSTR method, as follows.

    using System.Runtime.InteropServices;
    void UseSecretData(SecureString secret)
    {
        IntPtr bstr = Marshal.SecureStringToBSTR(secret);
        try
        {
            // Use the bstr here
        }
        finally
        {
            // Make sure that the clear text data is zeroed out.
            Marshal.ZeroFreeBSTR(bstr);
        }
    }
    
    

    As soon as you are finished with the data, make sure that you use the Marshal.ZeroFreeBSTR method to clear the text data.

    Note   Avoid converting back and forth between regular strings and secure strings because regular strings are immutable and you cannot clear them. As a result, you could have multiple copies of unencrypted strings in memory.

  • How to use DPAPI to protect sensitive data

    To use DPAPI to encrypt sensitive data in configuration files, use the DataProtectionConfigurationProvider class. To encrypt sensitive data with DPAPI in ASP.NET Web.config files, use Aspnet_regiis.exe. For Windows applications, use the protected configuration API. For more information, see the section, How to Encrypt Configuration Data in Configuration Files, in this document.

    To use DPAPI to encrypt data in other locations, such as the registry, use the ProtectedData class.

    using System.Security.Cryptography;
    
    // Define an entropy value (use of entropy is optional).
    byte[] entropy = new byte[] { 0x23, 0x06, 0x08, 0x09, 0x22, 0x03, 0x25 };
    // Get the original data in a byte array.
    byte[] toEncrypt = UnicodeEncoding.ASCII.GetBytes(
                       "The secret data to be encrypted");
    
    // Encrypt the data by the using ProtectedData class. This example uses
    // the local machine key store.
    byte[] encryptedData = ProtectedData.Protect(toEncrypt, entropy,
                           DataProtectionScope.LocalMachine);
    
    
  • How to crypto algorithms to encrypt data

    You can use classes in the System.Security.Cryptography namespace to encrypt data with the various cryptography algorithms. Use symmetric algorithms, such as RC2, TripleDES, and Rijndael to encrypt large chunks of data. Use asymmetric algorithms, such as RSA, to encrypt small amounts of data. Also, consider using asymmetric encryption to encrypt symmetric encryption keys.

    The following code shows how to encrypt data using symmetric encryption with the RijndaelManaged class:

    using System.Security.Cryptography;
    using System.IO;
    RijndaelManaged myRijndael = new RijndaelManaged();
    
    // Create a new key and initialization vector.
    // If a key is not provided, a key of appropriate length is
    // automatically generated. You can retrieve its value through the Key
    // property. Similarly, an initialization vector is automatically
    // generated if you do not specify one.
    myRijndael.GenerateKey();
    myRijndael.GenerateIV();
    // Get the key and IV.
    byte[] key = myRijndael.Key;
    byte[] iv = myRijndael.IV;
    
    // Get the encryptor.
    ICryptoTransform encryptor = myRijndael.CreateEncryptor(key, IV);
    // Define a new CryptoStream object to hold the encrypted bytes
    // and encrypt the data.
    MemoryStream msEncrypt = new MemoryStream();
    CryptoStream csEncrypt = new CryptoStream(msEncrypt, 
                                       encryptor, CryptoStreamMode.Write);
    // Convert the data to a byte array.
    ASCIIEncoding textConverter = new ASCIIEncoding();
    byte[] toEncrypt = textConverter.GetBytes("Secret data to encrypt");
    // Encrypt the data by writing it to the CryptoStream object.
    // Write all data to the crypto stream and flush it.
    csEncrypt.Write(toEncrypt, 0, toEncrypt.Length);
    csEncrypt.FlushFinalBlock(); 
    
    // Get encrypted array of bytes from the memory stream.
    byte[] encrypted = msEncrypt.ToArray();
    // Pass the encrypted data to a recipient. The recipient needs to know the
    // key and initialization vector used for encryption. You can also 
    // encrypt your secret key by using a public key algorithm, and then 
    // pass it to the message recipient with the encrypted message.
    
    // Get a decryptor that uses the same key and IV as the encryptor.
    ICryptoTransform decryptor = myRijndael.CreateDecryptor(key, iv);
    // Now decrypt the previously encrypted data using the decryptor.
    MemoryStream msDecrypt = new MemoryStream(encrypted);
    CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, 
                                               CryptoStreamMode.Read);
    byte[] fromEncrypt = new byte[encrypted.Length];
    
    // Read the data out of the crypto stream.
    csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length);
    
    // Convert the byte array back into a string.
    string roundtrip = textConverter.GetString(fromEncrypt);
    
    // Display the original data and the decrypted data.
    Console.WriteLine("Original:   {0}", original);
    Console.WriteLine("Round Trip: {0}", roundtrip);
    
    
  • How to use XML encryption to help protect sensitive data

    You can use the classes in the System.Security.Cryptography.Xml namespace, including the EncryptedXml class, to encrypt an entire XML document or an individual element within an XML document. For performance reasons, you should use a symmetric encryption algorithm, such as AES, to create a session key to encrypt the data; and then, if necessary, use an asymmetric encryption algorithm, such as RSA, to encrypt the symmetric session key. This enables you to transmit the session key in an encrypted format to a recipient. Only the recipient with the associated RSA private key can decrypt the session key and then decrypt the encrypted XML data.

    To use XML encryption, create a session key by using a symmetric algorithm, such as AES to encrypt the XML data. Then use an RSA public key to encrypt the AES session key. This ensures that only the holder of the associated private key can decrypt the data. Use the session key to encrypt the data with the EncryptedXml class. Create an EncryptedData element, and populate it with the encrypted cipher text. This lets the recipient know which algorithm was used for the encryption, and, optionally, the encrypted session key required to decrypt the data. Finally, replace the clear text element in the XML data with the EncryptedData element.

    For more information, see How to: Encrypt XML Elements with Asymmetric Keys.

    Note that using XML encryption requires the recipient of encrypted XML data to use an out-of-band mechanism to distribute his or her public key to any party who needs to be able to send encrypted data to the recipient. This is normally accomplished by using X.509 digital certificates.

  • How to use X.509 certificates to encrypt XML data

    If you are using digital certificates, you can use the X.509 certificate support in the System.Security.Cryptography.X509Certificates namespace to encrypt XML data.

    To use X.509 certificates, access the certificate from the certificate store, create a new instance of the EncryptedXml class, call its Encrypt method, and pass the certificate. Finally, replace the clear text XML element with the EncrytedData element.

    To be able to decrypt the data, you must have the private key associated with the public key in the X.509 certificate.

    For more information, see How to: Encrypt XML Elements with X.509 Certificates.

  • How to use XML signatures

    You can use XML digital signatures to help ensure the integrity and authenticity of origin of XML documents. You can use the classes in the System.Security.Cryptography.Xml namespace to sign an XML document or part of an XML document with a digital signature.

    The following code example shows how to use an RSA signing key to sign an XML document.

    using System.Security.Cryptography;
    using System.Security.Cryptography.Xml;
    using System.Xml;
    // Create a new RSA key container and save it in a key container.
    CspParameters cspParams = new CspParameters();
    cspParams.KeyContainerName = "MyRSASigningKey";
    // Create a new RSA signing key and save it in the container. 
    RSACryptoServiceProvider rsaKey = new 
                                  RSACryptoServiceProvider(cspParams);
    //   Create a SignedXml object and add the key to it.
    SignedXml signedXml = new SignedXml(doc);
    signedXml.SigningKey = Key;
    // Create a Reference object that describes what to sign.
    Reference reference = new Reference();
    reference.Uri = ""; // "" means sign the whole document
    // Add an enveloped transformation to the reference, add the reference 
    // to the signed XML document, and then compute the signature.
    XmlDsigEnvelopedSignatureTransform env = new
                                  XmlDsigEnvelopedSignatureTransform();
    reference.AddTransform(env);
    // Add the reference to the SignedXml object.
    signedXml.AddReference(reference);
    // Compute the signature.
    signedXml.ComputeSignature();
    //   Get the XML representation of the signature and save it in an XmlElement object. Append the element to the original XML document.
    XmlElement xmlDigitalSignature = signedXml.GetXml();
    // Append the element to the XML document.
    Doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
    
    

Code Access Security

Code access security is a resource-constraint model designed to restrict the types of system resources that code can access and the types of privileged operations that the code can perform. These restrictions are independent of the user who calls the code or the user account under which the code runs. You can use code access security to restrict what your code can do, and to restrict which code can call your code.

  • How to use the AllowPartiallyTrustedCallersAttribute attribute (APTCA)

    You can use APTCA to override the implicit link demand for full trust that is placed on every publicly accessible member of a strong-named assembly. APTCA is an assembly-level attribute, as shown in the following code example.

     [assembly: AllowPartiallyTrustedCallersAttribute()]
    
    

    You should use APTCA only when necessary because APTCA increases the attack surface and exposes your code to partial trust callers.

    Use APTCA only if you specifically want partially trusted callers to use your strong-named assembly. For example, you might need an application running on a file share to access your assembly located in the global assembly cache. Also, only use APTCA if you have performed a thorough security code review, and your code has been rigorously audited for security vulnerabilities. Examine the resource access and other privileged operations performed by your assembly, and then consider authorizing access to these operations by using other code access security demands.

  • How to use code access security

    You can use code access security to restrict what your code can do, to restrict which code can call your code and to create a restricted execution environment for untrusted code like plug-ins.

    For example, in hosted environments, you can use ASP.NET code access security policy to isolate applications from one another and from accessing shared system resources. For more information, see How To: Use Medium Trust in ASP.NET 2.0. Restricting what code can do is also useful when you need to run untrusted code, such as third-party components and plug-ins. For more information, see the section, How to Use Separate Application Domains to Isolate Untrusted Code, in this document.

    You can use permission demands to help ensure that only code with the appropriate permissions can call your code.

    You do not need to be concerned with code access security if your code is locally installed because code installed on a computer runs with full trust. Code access security is applicable in the following scenarios:

    • Using ClickOnce to deploy and execute applications.
    • Running partial trust ASP.NET Web applications, for example on hosted servers.
    • Running managed controls inside Internet Explorer.
    • Running code from an intranet file share.
    • Running code inside a Microsoft Office application.
    • Developing a class library to be used by other applications, including partial trust applications.
  • How to use Demand and Assert

    If you need to call the CodeAccessPermission. Assert method to prevent a demand from propagating beyond the current stack frame, consider demanding an alternate permission to authorize the calling code before calling CodeAccessPermission. Assert. For example, if you call an unmanaged library and need to assert the unmanaged code permission, consider demanding another built-in or custom permission first.

    If you only need to call Assert to satisfy the demands of a single method that your code calls, then place Assert prior to the method call. Then immediately call the RevertAssert method to keep the assertion window as small as possible, and to ensure that any subsequent code your method calls does not inadvertently succeed because Assert is still in effect. A common practice is to place the call to RevertAssert in a finally block to ensure that it always gets called, even in the event of an exception.

  • How to use security transparency

    If you need to make an entire assembly transparent to indicate that the assembly does not contain any critical code and does not elevate the privileges of the call stack in any way, you can explicitly add transparency to an assembly with the following attribute:

     [assembly: SecurityTransparent]
    
    

    If you need to mix critical and transparent code in the same assembly, start by marking the assembly with the System.Security.SecurityCriticalAttribute attribute to indicate that the assembly can contain critical code, as shown here.

     [assembly: SecurityCritical]
    
    

    If you want to perform security-critical actions, you must explicitly mark the code that will perform the critical action with another SecurityCritical attribute, as shown in the following code example.

     [assembly: SecurityCritical]
    public class A
    {
        [SecurityCritical]
        public void Critical()
        {
            // critical
        }
    
        public int SomeProp
        {
            get {/* transparent */ }
            set {/* transparent */ }
        }
    }
    public class B
    {    
        internal string SomeOtherProp
        {
            get { /* transparent */ }
            set { /* transparent */ }
        }
    }
    
    

    The above code is transparent (this is the default setting, even with the assembly-level SecurityCritical attribute) except for the Critical method, which is explicitly marked as critical.

  • How to use declarative and imperative permission requests

    You can use declarative principal-permission demands on methods when you want to authorize access to those methods. The following attribute ensures that only users who are members of the Manager role can call the GetCustomerDetails method.

    using System.Security.Permissions;
    using System.Threading;
    ...
    
    [PrincipalPermissionAttribute(SecurityAction.Demand, Role="Manager")]
    public void GetCustomerDetails(int CustId)
    {
    }
    
    

    If you need more detailed control and you need to perform authorization within a method, then you can perform imperative principal-permission demands or explicit role checks on a block of code. The following code example shows how to perform an imperative principal-permission demand.

    using System.Security;
    using System.Security.Permissions;
    
    public void GetCustomerDetails(int CustId)
    {
        try
        {
            // Imperative principal permission role check to verify
            // that the caller is a manager.
            PrincipalPermission principalPerm = new PrincipalPermission(null, 
                                                    "Manager");
            principalPerm.Demand();
            // Code that follows is only executed if the caller is a
            // member of the "Manager" role.
        }
        catch( SecurityException ex )
        {
       . . .
        }
    }
    
    

    The following code example uses explicit role checks.

    public void GetCustomerDetails(int CustId)
    {
        if(!Thread.CurrentPrincipal.IsInRole("Manager"))
        {
        . . .
        }
    }
    
    
  • How to test code in a partial trust environment

    With Visual Studio 2005, you can debug applications in partial trust by using the permission settings defined on the Security tab of a project's properties. Specify the correct zone in the drop-down list.

    You can debug ASP.NET partial trust applications by setting the level attribute on the trust element in the Web.config file. The standard partial-trust levels available are High, Medium, Low, and Minimal. You can also create custom trust levels. For more information about how to do this, see How To: Use Code Access Security in ASP.NET 2.0.

    Note   You cannot debug applications that are configured to run at low or minimal trust levels.

  • How to use PermCalc to calculate permissions

    You use the permission calculator tool (also known as the minimum grant set determination tool) to calculate the code access security permissions that callers must be granted to access the public entry points of an assembly. To use Permcalc.exe, specify the assembly name to analyze together with any dependent assemblies.

    The following command reports the minimum permissions that the Myapp.exe application needs to run. It displays the output in the default viewer for XML files. The target file is Myapp.exe, and the dependent files are Mydep1.dll and Mydep2.dll.

    Permcalc.exe -sandbox -show myapp.exe mydep1.dll mydep2.dll

    The -sandbox switch reports the minimum-permission sandbox in which an application can run instead of the permissions required by callers of the assembly's entry points. The -show switch displays the output file when the calculations are complete.

    The following command reconstructs all cache files and includes call stacks for the Mylib.dll assembly. It displays the output in the default viewer for XML files.

    Permcalc.exe -cleancache -stacks -show mylib.dll

    In situations where exact permission values cannot be determined, the default behavior of the tool is to overestimate by using an unrestricted permission state. The following options can be helpful in such situations. Use the -Internet switch to cause the Internet zone permissions to be used as an estimate. Use the -Under switch to override default behavior and to force an underestimation of the permissions.

  • How to disable code access security while debugging a partially trusted application

    In the .NET Framework 1.1, you can permanently disable code access security. In .NET Framework 2.0, you can only do this temporarily. Temporarily disabling code access security can help you determine whether a problem is security related You can temporarily disable code access security by running CasPol.exe from a command line with the -s off switches, as shown here:

    CasPol.exe -s off

    This disables code access security temporarily while the CasPol process remains active. You can press ENTER when you want to re-enable code access security. When code access security is disabled, all code access demands succeed.

    Note   Disabling code access security opens your system to potential attack. You should only do this when debugging an application in a secured environment.

  • How to modify and deploy enterprise policy

    You can modify enterprise policy by creating a new permission set that contains only the permissions that your applications require, and then modify the All_Code code group to use the modified permission set.

    To modify and deploy enterprise policy:

    1. From Administrative Tools, select Microsoft .NET Framework 2.0 Configuration.
    2. In the Microsoft .NET Framework 2.0 Configuration dialog box, double-click (or expand) the tree view in the left pane and then double-click Runtime Security Policy.
    3. Double-click (or expand) Enterprise.
    4. Right-click Permission Sets, and then click New.
    5. In the Create Permission Set dialog box, either import a permission set from an XML file or create a new permission set. You can use the wizard to create the permission set by selecting the appropriate permissions that you want your applications to have.
    6. In the left pane, under the Enterprise node, double-click (or expand) Code Group.
    7. Select the All_Code node and in Tasks section of the right pane, click Edit Code Group Properties.
    8. In the All_Code Properties dialog box, click the Permission Set tab.
    9. In the Permission set drop-down box, select the newly created permission set and then click Apply.

    To deploy enterprise policy across your organization, you have two options. You can create an .msi file and deploy it by using group policy or SMS. Alternatively, you can use CasPol.exe to create batch file scripts to apply security policy changes. You run the scripts on each computer.

    To create an .msi deployment package:

    1. Right-click the Runtime Security Policy node and click Create Deployment Package.
    2. In the Deployment Package Wizard dialog box, select the security policy level to be deployed as enterprise.
    3. Specify the folder and file name of the MSI, and then click Next and Finish.
  • How to apply security policy to dynamically created assemblies

    To apply security policy to a dynamically created assembly, use the overload of the AppDomain.DefineDynamicAssembly method that accepts three permission sets and evidence as shown below.

    public sealed AssemblyBuilder DefineDynamicAssembly(
                AssemblyName name, 
                AssemblyBuilderAccess access, 
                Evidence evidence, 
                PermissionSet requiredPermissions, 
                PermissionSet optionalPermissions, 
                PermissionSet refusedPermissions
    );
    
    

    You can pass required, optional, and refused permission sets to apply security policy to the dynamic assembly. Passing evidence forces the CLR to evaluate the permission set for the created dynamic assembly.

    Note   If you use an overload that does not accept evidence, then the CLR does not evaluate the permission set for the dynamic assembly. Instead the permission set is inherited from the permission set of the assembly that is emitting the code.

  • How to use separate application domains to isolate untrusted code

    In the .NET Framework 1.1, creating a new application domain with restricted permissions requires you to define code groups and the permission sets that would be granted to each of those groups. In the .NET Framework 2.0, you can use an API to eliminate the need for boilerplate code and simplify setting up a constrained execution environment (or sandbox) for an application domain. An application domain created with this API loads assemblies with two grant sets. Either the assembly is fully trusted, or it is granted the permission set passed through the grantSet parameter.

    The following code example shows how you can create a sandboxed application domain by using an API in the .NET Framework 2.0.

    using System.Security;
    using System.Security.Permissions;
    using System.Threading;
    using System.Security.Policy;
    // Get the public key blob of the assembly to which you want to grant full trust.
    byte[] publicKey = assemblyName.GetPublicKey();
    if (publicKey == null || publicKey.Length == 0)
        throw new InvalidOperationException("Assembly is not strongly named");
    
    StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);
    
    // Create the StrongName.
    StrongName trustedAssemblyStrongName = new StrongName(keyBlob, assemblyName.Name, assemblyName.Version);
    
    // Set up evidence for the untrusted code.
    Evidence untrustedEvidence = new Evidence(
              new object[] { new Zone(SecurityZone.Untrusted ) },
              new object[] { });
    
    // Create the permission set. Deny all permissions by default
    // and explicitly add the permissions that meet your requirements.
    PermissionSet permissions = new PermissionSet(PermissionState.None);
    
    // Grant access to the arbitrary target assembly to be executed.
    permissions.AddPermission(
             new FileIOPermission(FileIOPermissionAccess.AllAccess, @"c:\sandbox\TargetExecutableAssembly.exe"));
    
    // Grant read access to myfile1.txt.
    permissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, @"c:\sandbox\myfile1.txt"));
    
    // Grant the sandboxed AppDomain permission to run managed code.
    permissions.AddPermission(new 
                        SecurityPermission(SecurityPermissionFlag.Execution));
    // This example uses a console application, so it requires UIPermission.
    permissions.AddPermission(new UIPermission(PermissionState.Unrestricted ));
    
    
    AppDomainSetup setup = new AppDomainSetup();
    // The base assembly directory for the sandboxed AppDomain is the
    // ApplicationBase of Current AppDomain.
    setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
    
    // New overload of AppDomain.CreateDomain in .NET 2.0
    // allows easy setup of sandboxed appdomains.
    AppDomain sandboxedDomain = AppDomain.CreateDomain(
                    "Sandboxed AppDomain", untrustedEvidence, setup,
                     permissions, new StrongName[] { trustedAssemblyStrongName });
    
    sandboxedDomain.ExecuteAssembly(@"C:\sandbox\TargetExecutableAssembly.exe");
    
    

Code Analysis

You can use automated code-analysis tools to help with your security code reviews. These tools help to identify initial security issues and to discover areas where additional security issues are likely to be discovered. For more information about security code reviews, see How To: Perform a Security Code Review for Managed Code (.NET Framework 2.0).

  • How to use FxCop

    FxCop is now integrated into Visual Studio 2005.

    To enable code analysis using FxCop:

    1. Open the project's Properties sheet and then click the Code Analysis tab.
    2. On the Code Analysis tab, select the Enable CodeAnalysis check box.
    3. Optionally, in the Rules section, exclude various design-guideline rules or change the status of specific design-guideline warnings to errors.

    After you enable code analysis, any FxCop warnings that are generated for the source code will appear in the error list.

Companion Guidance

The following companion guidance is listed in the order it appears in the document to make it easier to print.

Additional Resources

Feedback

Provide feedback by using either a Wiki or e-mail:

We are particularly interested in feedback regarding the following:

  • Technical issues specific to recommendations
  • Usefulness and usability issues

Technical Support

Technical support for the Microsoft products and technologies referenced in this guidance is provided by Microsoft Support Services. For product support information, please visit the Microsoft Product Support Web site at https://support.microsoft.com.

Community and Newsgroups

Community support is provided in the forums and newsgroups:

To get the most benefit, find the newsgroup that corresponds to your technology or problem. For example, if you have a problem with ASP.NET security features, you would use the ASP.NET Security forum.

Contributors and Reviewers

  • External Contributors and Reviewers: Jason Taylor, Security Innovation; Rudolph Araujo, Foundstone Professional Services
  • Microsoft Product Group: Charlie Kaufman, Mike Downen, Pablo Castro, Stefan Schackow
  • Microsoft Services and PSS Contributors and Reviewers: Tom Christian, Wade Mascia
  • Microsoft patterns & practices Contributors and Reviewers: Carlos Farre
  • Test team: Larry Brader, Microsoft Corporation; Nadupalli Venkata Surya Sateesh, Sivanthapatham Shanmugasundaram, Infosys Technologies Ltd.
  • Edit team: Lara Ballinger, Microsoft Corporation
  • Release Management: Sanjeev Garg, Microsoft Corporation

patterns & practices Developer Center

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.