Seguridad de acceso del código y ADO.NET

Actualización: November 2007

.NET Framework ofrece seguridad basada en funciones y seguridad de acceso del código (CAS); ambas se implementan utilizando una infraestructura común proporcionada por Common Language Runtime (CLR). En el mundo del código no administrado, la mayoría de las aplicaciones se ejecutan mediante los permisos del usuario o de la entidad de seguridad. Por consiguiente, los sistemas de equipos pueden resultar dañados y se pueden poner en peligro los datos privados si un usuario con un nivel elevado de privilegios ejecuta software malintencionado o que contenga errores.

El código administrado que se ejecuta en .NET Framework, sin embargo, incluye seguridad de acceso del código, que se aplica únicamente al código. El permiso para que el código se ejecute o no depende de su origen o de otros aspectos de la identidad del código, y no sólo de la identidad de la entidad de seguridad. De esta forma, se reduce la probabilidad de que se utilice el código de manera incorrecta.

Permisos de acceso a código

Cuando se ejecuta el código, éste presenta evidencia de que lo evalúa el sistema de seguridad Common Language Runtime CLR. Este tipo de evidencia normalmente contiene el origen del código, incluidos la dirección URL, el sitio, la zona y las firmas digitales que determinan la identidad del ensamblado.

CLR permite que el código realice únicamente las operaciones para las que tiene permiso. El código puede solicitar permisos y las peticiones se aceptan en función de la directiva de seguridad que haya establecido un administrador.

Nota:

El código que se ejecuta en el CLR no se puede conceder permisos a sí mismo. Por ejemplo, el código puede solicitar y que se le asignen menos permisos de los que concede una directiva de seguridad, pero no se le concederán más permisos. A la hora de conceder permisos, comience sin ningún permiso y agregue los mínimos permisos necesarios para la tarea concreta que se lleva a cabo. Si se comienza con todos los permisos y posteriormente se deniegan permisos de forma individual, se obtienen aplicaciones poco seguras que pueden contener vacíos no intencionados en la seguridad debido a la concesión de más permisos de los que son necesarios. Para obtener más información, vea Configurar directivas de seguridad y Administración de directivas de seguridad.

Hay tres tipos de permisos de acceso a código:

  • Los permisos de acceso a código derivan de la clase CodeAccessPermission. Se requieren permisos para tener acceso a recursos protegidos, como archivos y variables de entorno, y para realizar operaciones protegidas, como el acceso a código no administrado.

  • Los permisos de identidad representan características que identifican un ensamblado. Los permisos se conceden al ensamblado en función de la evidencia, que puede incluir elementos como una firma digital o el origen del código. Los permisos de identidad también derivan de la clase base CodeAccessPermission.

  • Los permisos de seguridad basados en funciones se basan en el hecho de si una entidad de seguridad tiene una identidad especificada o si es miembro de una función especificada. La clase PrincipalPermission permite comprobar los permisos declarativos e imperativos con la entidad de seguridad activa.

Para determinar si el código tiene autorización para el acceso a un recurso o para ejecutar una operación, el sistema de seguridad en tiempo de ejecución atraviesa la pila de llamadas y compara los permisos concedidos a cada llamador con el permiso que se exige. Si algún llamador de la pila de llamadas no tiene el permiso exigido, se iniciará clase SecurityException y se rechazará el acceso.

Solicitar permisos

La finalidad de solicitar permisos es informar al motor en tiempo de ejecución de los permisos que necesita la aplicación para ejecutarse y garantizar que sólo recibe los permisos que realmente necesita. Por ejemplo, si la aplicación necesita acceso de escritura al disco local, requiere FileIOPermission. Si este permiso no se ha concedido, se producirán errores en la aplicación al intentar escribir en el disco. Sin embargo, si la aplicación solicita FileIOPermission y el permiso no se concede, generará la excepción al comienzo y no se cargará.

En un escenario donde la aplicación sólo necesita leer datos del disco, puede solicitar que nunca se le concedan permisos de escritura. En el caso de se produzca un error o un ataque malintencionado, el código no podrá dañar los datos con los que trabaja. Para obtener más información, vea Solicitar permisos.

Seguridad basada en funciones y CAS

La implementación de la seguridad basada en funciones y de la seguridad de acceso del código (CAS) mejora la seguridad global de la aplicación. La seguridad basada en funciones se puede basar en una cuenta de Windows o en una identidad personalizada, de forma que la información sobre la entidad de seguridad esté disponible en el subproceso actual. Además, a menudo se requiere a la aplicaciones que proporcionen acceso a datos o recursos basándose en credenciales proporcionadas por el usuario. Normalmente, dichas aplicaciones comprueban la función de un usuario y proporcionan acceso a los recursos basándose en dichas funciones.

La seguridad basada en funciones permite que un componente identifique los usuarios actuales y sus funciones asociadas en tiempo de ejecución. Esta información se asigna a continuación mediante una directiva CAS para determinar el conjunto de permisos que se conceden en tiempo de ejecución. En un dominio de aplicación especificado, el host puede cambiar la directiva de seguridad predeterminada basada en funciones y establecer una entidad de seguridad que represente a un usuario y a las funciones asociadas al usuario.

CLR usa permisos para implementar su mecanismo a fin de aplicar restricciones en el código administrado. Los permisos de seguridad basada en funciones proporcionan un mecanismo para descubrir si un usuario (o el agente que actúa en su nombre) tiene una identidad concreta o es miembro de una función especificada. Para obtener más información, vea Permisos de seguridad.

En función del tipo de aplicación que cree, deberá considerar también la posibilidad de implementar permisos basados en funciones en la base de datos. Para obtener más información sobre la seguridad basada en funciones en SQL Server, vea Seguridad de SQL Server (ADO.NET).

Ensamblados

Los ensamblados componen la unidad fundamental de implementación, control de versiones, reutilización, ámbito de activación y permisos de seguridad en una aplicación de .NET Framework. Un ensamblado proporciona una colección de tipos y recursos creados para funcionar en conjunto y formar una unidad lógica de funcionalidad. En CLR, un tipo no existe si no es en el contexto de un ensamblado. Para obtener más información sobre la creación e implementación de ensamblados, vea Programar con ensamblados.

Ensamblados con nombre seguro

Un nombre seguro, o firma digital, consta de la identidad del ensamblado, que incluye su nombre de texto sencillo, el número de versión, la información de referencia cultural (si se proporciona), así como una clave pública y una firma digital. La firma digital se genera a partir de un archivo de ensamblado que usa la clave privada correspondiente. El archivo de ensamblado contiene el manifiesto del ensamblado, que contiene los nombres y códigos hash de todos los archivos que forman el ensamblado.

La asignación de un nombre seguro al ensamblado proporciona a una aplicación o a un componente una identidad única que puede usar otro software para referirse a ella de manera explícita. La asignación de nombres seguros protege a los ensamblados de la suplantación por parte de un ensamblado que contenga código malintencionado. También garantiza la coherencia entre las diferentes versiones de un componente. Debe usar nombres seguros en los ensamblados que se van a implementar en la caché de ensamblados global (GAC). Para obtener más información, vea Crear y utilizar ensamblados con nombre seguro.

Confianza parcial en ADO.NET 2.0

En ADO.NET 2.0, se pueden ejecutar los proveedores de datos de .NET Framework para SQL Server, OLE DB, ODBC y para Oracle en entornos de confianza parcial. En versiones anteriores de .NET Framework, sólo se admitía el uso de System.Data.SqlClient en aplicaciones que no fuesen de plena confianza.

Una aplicación de confianza parcial que utilice el proveedor de SQL Server debe tener como mínimo permisos de ejecución y SqlClientPermission.

Propiedades de atributos de permiso de confianza parcial

En las situaciones de confianza parcial, se puede aplicar SqlClientPermissionAttribute a los miembros a fin de restringir aún más las capacidades disponibles para el proveedor de datos de .NET Framework para SQL Server.

Nota:

El proveedor de datos de .NET Framework para SQL Server necesita permiso de plena confianza para abrir una SqlConnection con la depuración de SQL habilitada en SQL Server 2000. La depuración de SQL para SQL Server 2005 no utiliza esta clase. Para obtener más información, vea los Libros en pantalla de SQL Server 2005.

En la siguiente tabla se muestran y se describen las propiedades SqlClientPermissionAttribute disponibles:

Propiedad de atributo de permiso

Descripción

Action

Obtiene o establece una acción de seguridad. Se hereda de SecurityAttribute.

AllowBlankPassword

Habilita o deshabilita el uso de una contraseña en blanco en una cadena de conexión. Los valores válidos son true (para habilitar el uso de contraseñas en blanco) y false (para deshabilitarlo). Se hereda de DBDataPermissionAttribute.

ConnectionString

Identifica una cadena de conexión admitida. Se pueden identificar varias cadenas de conexión.

Nota:
No incluya un id. de usuario o una contraseña en la cadena de conexión. En esta versión no se pueden modificar las restricciones de las cadenas de conexión mediante la herramienta Configuración de .NET Framework.

Se hereda de DBDataPermissionAttribute.

KeyRestrictions

Identifica qué parámetros de las cadenas de conexión están permitidos o no lo están. Los parámetros de las cadenas de conexión se identifican con el formato <nombre de parámetro>=. Se pueden especificar varios parámetros separados por punto y coma (;).

Nota:
Si no especifica KeyRestrictions, pero establece la propiedad KeyRestrictionBehavior en AllowOnly o PreventUsage, no se permitirán parámetros de cadena de conexión adicionales. Se hereda de DBDataPermissionAttribute.

KeyRestrictionBehavior

Identifica los parámetros de cadenas de conexión como los únicos parámetros adicionales permitidos (AllowOnly), o bien identifica los parámetros adicionales no permitidos (PreventUsage). AllowOnly es el valor predeterminado. Se hereda de DBDataPermissionAttribute.

TypeID

Obtiene un identificador único para el atributo cuando se implementa en una clase derivada. Se hereda de Attribute.

Unrestricted

Indica si se declaran permisos protegidos para el origen. Se hereda de SecurityAttribute.

Sintaxis de ConnectionString

En el siguiente ejemplo se muestra cómo se utiliza el elemento connectionStrings de un archivo de configuración para permitir únicamente el uso de una determinada cadena de conexión. Para obtener más información sobre el almacenamiento de cadenas de conexión en archivos de configuración y recuperación de las mismas, vea Cadenas de conexión (ADO.NET).

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;" />
</connectionStrings>

Sintaxis de KeyRestrictions

En el ejemplo siguiente se habilita la misma cadena de conexión, se habilita el uso de las opciones de cadena de conexión Encrypt y PacketSize, pero se restringe el uso de otras opciones de cadena de conexión.

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Encrypt=;Packet Size=;"
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Sintaxis de KeyRestrictionBehavior con PreventUsage

En el ejemplo siguiente se habilita la misma cadena de conexión y se permiten todos los demás parámetros de conexión, excepto User Id, Password y Persist Security Info.

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="User Id=;Password=;Persist Security Info=;"
    KeyRestrictionBehavior="PreventUsage" />
</connectionStrings>

Sintaxis de KeyRestrictionBehavior con AllowOnly

En el ejemplo siguiente se habilitan dos cadenas de conexión que contienen los parámetros Initial Catalog, Connection Timeout, Encrypt y Packet Size. El resto de los parámetros de cadenas de conexión están restringidos.

<connectionStrings>
  <add name="DatabaseConnection" 
    connectionString="Data Source=(local);Initial 
    Catalog=Northwind;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;" 
    KeyRestrictionBehavior="AllowOnly" />

  <add name="DatabaseConnection2" 
    connectionString="Data Source=SqlServer2;Initial 
    Catalog=Northwind2;Integrated Security=true;"
    KeyRestrictions="Initial Catalog;Connection Timeout=;
       Encrypt=;Packet Size=;" 
    KeyRestrictionBehavior="AllowOnly" />
</connectionStrings>

Habilitar confianza parcial con un conjunto de permisos personalizados

Para habilitar el uso de permisos System.Data.SqlClient para una zona determinada, un administrador del sistema debe crear un conjunto de permisos personalizados y establecerlo como el conjunto de permisos de dicha zona. Los conjuntos de permisos predeterminados, como LocalIntranet, no se pueden modificar. Por ejemplo, para incluir permisos System.Data.SqlClient para un código que tiene una Zone de LocalIntranet, un administrador del sistema tendría que copiar el conjunto de permisos para LocalIntranet, cambiar el nombre a "CustomLocalIntranet", agregar los permisos System.Data.SqlClient, importar el conjunto de permisos CustomLocalIntranet mediante Herramienta de la directiva de seguridad de acceso a código (Caspol.exe) y establecer el conjunto de permisos de LocalIntranet_Zone en CustomLocalIntranet.

Conjunto de permisos de ejemplo

A continuación se muestra un ejemplo de un conjunto de permisos para el proveedor de datos de .NET Framework para SQL Server en un escenario que no es de plena confianza. Para obtener información sobre la creación de conjuntos de permisos, vea Configurar conjuntos de permisos mediante Caspol.exe.

<PermissionSet class="System.Security.NamedPermissionSet"
  version="1"
  Name="CustomLocalIntranet"
  Description="Custom permission set given to applications on
    the local intranet">

<IPermission class="System.Data.SqlClient.SqlClientPermission, System.Data, Version=2.0.0000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Integrated Security=true;"
 KeyRestrictions="Initial Catalog=;Connection Timeout=;
   Encrypt=;Packet Size=;" 
 KeyRestrictionBehavior="AllowOnly" />
 </IPermission>
</PermissionSet>

Comprobar el acceso a código de ADO.NET mediante permisos de seguridad

En las situaciones de confianza parcial, puede solicitar privilegios de seguridad de acceso del código para determinados métodos en el código mediante la especificación de SqlClientPermissionAttribute. Si la directiva de seguridad restringida no permite el privilegio, se inicia una excepción antes de ejecutarse el código. Para obtener más información sobre directivas de seguridad, vea Administración de directivas de seguridad y Procedimientos recomendados para directivas de seguridad.

Ejemplo

En el siguiente ejemplo se muestra cómo escribir código que requiera una determinada cadena de conexión. Simula la denegación de permisos sin restricciones para System.Data.SqlClient, que podría implementar un administrador del sistema en una situación real mediante una directiva de seguridad CAS.

Nota de seguridad:

Al diseñar permisos de seguridad CAS en ADO.NET, el procedimiento correcto es comenzar con el caso más restrictivo (sin ningún permiso) y agregar a continuación los permisos específicos necesarios para la tarea determinada que el código debe realizar. El modelo opuesto, que comienza con todos los permisos y deniega a continuación un permiso concreto, no es seguro puesto que existen muchas formas de expresar la misma cadena de conexión. Por ejemplo, si comienza con todos los permisos y después intenta denegar el uso de la cadena de conexión "servidor=unServidor", la cadena "servidor=unServidor.miEmpresa.com" seguirá obteniendo permiso. Al comenzar siempre por no conceder ningún permiso, se reduce la posibilidad de que haya lagunas en el conjunto de permisos.

En el siguiente código se muestra cómo SqlClient realiza la petición de seguridad, que inicia SecurityException si los permisos de seguridad CAS adecuados no se encuentran en su lugar. El resultado SecurityException se muestra en la ventana de la consola.

Private Sub TestCAS(ByVal connectString1 As String, ByVal connectString2 As String)
    ' Simulate removing SqlClient permissions.
    Dim permission As New SqlClientPermission(PermissionState.Unrestricted)
    permission.Deny()

    ' Try to open a connection.
    Try
        Using connection As New SqlConnection(connectString1)
            connection.Open()
            Console.WriteLine("Connection opened, unexpected.")
        End Using

    Catch ex As System.Security.SecurityException
        Console.WriteLine("Failed, as expected: {0}", _
            ex.FirstPermissionThatFailed)

        ' Uncomment the following line to see Exception details.
        ' Console.WriteLine("BaseException: {0}", ex.GetBaseException())
    End Try

    SqlClientPermission.RevertAll()

    ' Add permission for a specific connection string.
    ' This would typically be achieved by the administrator  
    ' deploying a CAS policy, not in your code.
    permission = New SqlClientPermission(PermissionState.None)
    permission.Add(connectString1, "", KeyRestrictionBehavior.AllowOnly)

    permission.PermitOnly()

    ' Try again, it should succeed now.
    Try
        Using connection As New SqlConnection(connectString1)
            connection.Open()
            Console.WriteLine("Connection opened, as expected.")
        End Using

    Catch ex As System.Security.SecurityException
        Console.WriteLine("Unexpected failure: {0}", ex.Message)
    End Try

    ' Try a different connection string. This should fail.
    Try
        Using connection As New SqlConnection(connectString2)
            connection.Open()
            Console.WriteLine("Connection opened, unexpected.")
        End Using

    Catch ex As System.Security.SecurityException
        Console.WriteLine("Failed, as expected: {0}", ex.Message)
    End Try
End Sub
static void TestCAS(string connectString1, string connectString2)
{
    // Simulate removing SqlClient permissions.
    SqlClientPermission permission = new 
        SqlClientPermission(PermissionState.Unrestricted);
    permission.Deny();

    // Try to open a connection.
    try
    {
        using (SqlConnection connection = new 
                   SqlConnection(connectString1))
        {
            connection.Open();
            Console.WriteLine("Connection opened, unexpected.");
        }
    }
    catch (System.Security.SecurityException ex)
    {
        Console.WriteLine("Failed, as expected: {0}", 
            ex.FirstPermissionThatFailed);

        // Uncomment the following line to see Exception details.
        //Console.WriteLine("BaseException: " + ex.GetBaseException());
    }

    SqlClientPermission.RevertAll();

    // Add permission for a specific connection string.
    // This would typically be achieved by the administrator deploying 
    // a CAS policy, not in your code.
    permission = new SqlClientPermission(PermissionState.None);
    permission.Add(connectString1,"", KeyRestrictionBehavior.AllowOnly);

    permission.PermitOnly();

    // Try again, it should succeed now.
    try
    {
        using (SqlConnection connection = new SqlConnection(connectString1))
        {
            connection.Open();
        }
        Console.WriteLine("Connection opened, as expected.");
    }
    catch (System.Security.SecurityException ex)
    {
        Console.WriteLine("Unexpected failure: {0}", ex.Message);
    }

    // Try a different connection string. This should fail.
    try
    {
        using (SqlConnection connection = new SqlConnection(connectString2))
        {
            connection.Open();
        }
        Console.WriteLine("Connection opened, unexpected.");
    }
    catch (System.Security.SecurityException ex)
    {
        Console.WriteLine("Failed, as expected: {0}", ex.Message);
    }
}

Debe poder ver este resultado en la ventana de la consola:

Failed, as expected: <IPermission class="System.Data.SqlClient.
SqlClientPermission, System.Data, Version=2.0.0.0, 
  Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1"
  AllowBlankPassword="False">
<add ConnectionString="Data Source=(local);Initial Catalog=
  Northwind;Integrated Security=SSPI" KeyRestrictions=""
KeyRestrictionBehavior="AllowOnly"/>
</IPermission>

Connection opened, as expected.
Failed, as expected: Request failed.

Interoperabilidad de código no administrado

El código que se ejecuta fuera de CLR se denomina código no administrado. Por lo tanto, los mecanismos de seguridad como CAS no se pueden aplicar en código no administrado. Los componentes COM, las interfaces ActiveX y las funciones de la API de Win32 son ejemplos de código no administrado. Cuando se ejecuta código no administrado se aplican consideraciones de seguridad especiales, de forma que no se ponga en peligro la seguridad global de la aplicación. Para obtener más información, vea Interoperar con código no administrado.

.NET Framework también es compatible con versiones anteriores de componentes COM existentes mediante el acceso a través de la interoperabilidad COM. Se pueden incluir componentes COM en una aplicación de .NET Framework usando las herramientas de la interoperabilidad COM para importar los tipos COM necesarios. Una vez que se han importado, los tipos COM están listos para su uso. La interoperabilidad COM también permite que los clientes COM tengan acceso a código administrado mediante la exportación de metadatos de ensamblado a una biblioteca de tipos y mediante el registro del componente administrado como un componente COM. Para obtener más información, vea Interoperabilidad COM avanzada.

Vea también

Otros recursos

Proteger aplicaciones de ADO.NET

Seguridad del código nativo y del código de .NET Framework

Seguridad de acceso a código

Seguridad basada en funciones