Optimizaciones de seguridad
Las comprobaciones de seguridad pueden producir problemas en algunas aplicaciones. Para mejorar el rendimiento se pueden utilizar dos técnicas de optimización. Una técnica combina las peticiones de seguridad y la otra suprime las peticiones de permiso para llamar a código no administrado. Aunque estas técnicas pueden mejorar el rendimiento de la aplicación, también pueden abrir puntos vulnerables a los ataques en el sistema de seguridad. Antes de utilizar estas técnicas de optimización, deberá tomar las precauciones siguientes:
Siga las instrucciones de codificación segura para código administrado.
Asegúrese de que conoce las repercusiones con respecto a la seguridad de las optimizaciones y utilice otros métodos para proteger la aplicación, según corresponda.
Implemente las optimizaciones de seguridad mínimas necesarias para mejorar el rendimiento de la aplicación.
Una vez optimizado el código, deberá probarlo para determinar si su rendimiento ha mejorado realmente. En caso contrario, quite las optimizaciones de seguridad para ayudar a evitar los puntos débiles involuntarios en el sistema de seguridad.
Precaución |
---|
Para optimizar la seguridad es necesario cambiar la seguridad de acceso a código estándar. Para evitar introducir puntos vulnerables en la seguridad del código, asegúrese de que conoce las repercusiones de seguridad de las técnicas de optimización antes de utilizarlas. |
Combinar peticiones de seguridad
Para optimizar el código que realiza peticiones de seguridad, en ocasiones se puede utilizar una técnica mediante la cual se combinan las peticiones.
Por ejemplo, si:
el código realiza varias operaciones en un solo método y,
al realizar cada una de estas operaciones, el código llama a una biblioteca de clases administrada que exige que el código tenga el mismo permiso en cada llamada a la biblioteca,
entonces:
- se puede modificar el código de manera que llame a los métodos Demand y Assert para ese permiso y así reducir la sobrecarga que suponen las peticiones de seguridad.
Si la profundidad de la pila de llamadas sobre el método es grande, esta técnica puede proporcionar una mejora importante del rendimiento.
Para ilustrar cómo funciona, supongamos que el método M realiza 100 operaciones. Cada operación llama a una biblioteca que realiza una petición de seguridad que exige que el código y todos sus llamadores tengan el permiso X. Debido a las peticiones de seguridad, cada operación hace que el motor en tiempo de ejecución recorra toda la pila de llamadas para examinar los permisos de cada llamador y determinar si el permiso X se concedió realmente a cada uno de ellos. Si la pila de llamadas sobre el método M tiene una profundidad de n niveles, se requieren 100n comparaciones.
Para optimizar, puede seguir estos procedimientos en el método M:
Exigir X, lo que hace que el motor en tiempo de ejecución realice un recorrido de pila (de profundidad n) para garantizar que todos los llamadores tengan realmente el permiso X.
Después, afirmar el permiso X, que hace que los recorridos de pila posteriores se detengan en el método M y se realicen correctamente, lo que reduce el número de comparaciones de permiso en 99n.
En el siguiente ejemplo de código, el método GetFileCreationTime
utiliza como parámetro una representación de cadena de un directorio y muestra el nombre así como la fecha de creación de cada archivo incluido en ese directorio. El método estático File.GetCreationTime lee la información de estos archivos, pero requiere una petición y un recorrido de pila para cada uno de los archivos que lee. El método crea una nueva instancia del objeto FileIOPermission, realiza una petición para comprobar los permisos de todos los llamadores de la pila y, a continuación, afirma el permiso si el resultado de la petición es satisfactorio. Si la petición se realiza correctamente, se ejecuta un solo recorrido de pila y el método lee la hora de creación de cada archivo del directorio que se ha pasado.
using System;
using System.IO;
using System.Security;
using System.Security.Permissions;
namespace OptimizedSecurity
{
public class FileUtil
{
public FileUtil()
{
}
public void GetFileCreationTime(string Directory)
{
//Initialize DirectoryInfo object to the passed directory.
DirectoryInfo DirFiles = new DirectoryInfo(Directory);
//Create a DateTime object to be initialized below.
DateTime TheTime;
//Get a list of files for the current directory.
FileInfo[] Files = DirFiles.GetFiles();
//Create a new instance of FileIOPermission with read
//permission to the current directory.
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.Read, Directory);
try
{
//Check the stack by making a demand.
FilePermission.Demand();
//If the demand succeeded, assert permission and
//perform the operation.
FilePermission.Assert();
for(int x = 0 ; x<= Files.Length -1 ; x++)
{
TheTime = File.GetCreationTime(Files[x].FullName);
Console.WriteLine("File: {0} Created: {1:G}", Files[x].Name,TheTime );
}
// Revert the Assert when the operation is complete.
CodeAccessPermission.RevertAssert();
}
//Catch a security exception and display an error.
catch(SecurityException)
{
Console.WriteLine("You do not have permission to read this directory.");
}
}
}
}
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Namespace OptimizedSecurity
Public Class FileUtil
Public Sub New()
End Sub
Public Sub GetFileCreationTime(Directory As String)
'Initialize DirectoryInfo object to the passed directory.
Dim DirFiles As New DirectoryInfo(Directory)
'Create a DateTime object to be initialized below.
Dim TheTime As DateTime
'Get a list of files for the current directory.
Dim Files As FileInfo() = DirFiles.GetFiles()
'Create a new instance of FileIOPermission with read
'permission to the current directory.
Dim FilePermission As New FileIOPermission(FileIOPermissionAccess.Read, Directory)
Try
'Check the stack by making a demand.
FilePermission.Demand()
'If the demand succeeded, assert permission and
'perform the operation.
FilePermission.Assert()
Dim x As Integer
For x = 0 To Files.Length - 1
TheTime = File.GetCreationTime(Files(x).FullName)
Console.WriteLine("File: {0} Created: {1:G}", Files(x).Name, TheTime)
Next x
' Revert the Assert when the operation is complete.
CodeAccessPermission.RevertAssert()
'Catch a security exception and display an error.
Catch
Console.WriteLine("You do not have permission to read this directory.")
End Try
End Sub
End Class
End Namespace
Si la petición del ejemplo anterior se realiza correctamente, se mostrarán todos los archivos, con su fecha y hora de creación, correspondientes al directorio que se ha pasado. Si la petición no se realiza correctamente, se interceptará la excepción de seguridad y se mostrará el siguiente mensaje en la consola:
You do not have permission to read this directory.
Suprimir peticiones de permiso de código no administrado
Existe una optimización especial para el código que tiene permiso para llamar a código no administrado. Esta optimización permite que el código administrado llame a código no administrado evitando la sobrecarga de un recorrido de pila. La aserción del permiso de código no administrado puede reducir el recorrido de pila, pero la optimización que se describe en este tema puede eliminarlo por completo. (Para obtener más información sobre el permiso para llamar a código no administrado, vea SecurityPermission).
Normalmente, una llamada a código no administrado genera una petición de permiso de código no administrado, lo que genera un recorrido de pila que determina si todos los llamadores tienen permiso para llamar a código no administrado. Al aplicar el atributo personalizado SuppressUnmanagedCodeSecurityAttribute al método que llama a código no administrado, se suprime la petición. Este atributo reemplaza el recorrido de pila completo en tiempo de ejecución por una comprobación que sólo verifica los permisos del llamador inmediato en el momento de la vinculación. De hecho, al utilizar este atributo, se abre una puerta al código no administrado. Sólo el código que tiene permiso de código no administrado puede utilizar este atributo; en caso contrario, no tiene efecto.
Precaución |
---|
Use con extremo cuidado el atributo SuppressUnmanagedCodeSecurityAttribute. Si lo utiliza incorrectamente, puede generar puntos débiles en la seguridad. El atributo SuppressUnmanagedCodeSecurityAttribute no debe utilizarse nunca para permitir que código de menor confianza (código que no posee permiso de código no administrado) llame a código no administrado. |
Se recomienda aplicar este atributo sólo a puntos de entrada a código no administrado declarados de forma privada, de manera que el código de otros ensamblados no pueda obtener acceso a la supresión de seguridad y sacar provecho de sus ventajas. Normalmente, el código administrado de gran confianza que utiliza este atributo exige primero algún permiso de los llamadores antes de invocar código no administrado por parte del llamador.
En el ejemplo siguiente, se muestra la aplicación del atributo SuppressUnmanagedCodeSecurityAttribute a un punto de entrada privado.
<SuppressUnmanagedCodeSecurityAttribute()> Private Declare Sub
EntryPoint Lib "some.dll"(args As String)
[SuppressUnmanagedCodeSecurityAttribute()]
[DllImport("some.dll")]
private static extern void EntryPoint(string args);
Si, por alguna casualidad, existiese código no administrado que fuese totalmente seguro en todas las circunstancias posibles, se podría exponer directamente un método con el atributo SuppressUnmanagedCodeSecurityAttribute a otro código administrado haciéndolo público en lugar de privado. Si decide exponer un método que tiene el atributo SuppressUnmanagedCodeSecurityAttribute, no sólo debe ser segura la funcionalidad del código no administrado sino que también debe ser insensible a los ataques de los llamadores malignos. Por ejemplo, el código debe funcionar de manera apropiada incluso cuando se creen argumentos imprevistos destinados específicamente a generar errores en el código.
Utilizar reemplazos declarativos y peticiones imperativas
Las aserciones y otros reemplazos son más rápidos cuando se realizan declarativamente, mientras que las peticiones son más rápidas cuando se realizan imperativamente. Aunque las ventajas de rendimiento pueden no ser arrolladoras, el uso de reemplazos declarativos y peticiones imperativas puede contribuir a mejorar el rendimiento del código.
Vea también
Referencia
File.GetCreationTime Method
SecurityPermission Class
SuppressUnmanagedCodeSecurityAttribute Class
Conceptos
Escribir bibliotecas de clases seguras