Proteger las cadenas de conexión y otros datos de configuración (C#)
por Scott Mitchell
Una aplicación ASP.NET normalmente almacena información de configuración en un archivo Web.config. Parte de esta información es confidencial y garantiza la protección. De forma predeterminada, este archivo no se servirá a un visitante del sitio web, pero un administrador o hacker pueden obtener acceso al sistema de archivos del servidor web y ver el contenido del archivo. En este tutorial, aprendemos que ASP.NET 2.0 nos permite proteger la información confidencial mediante el cifrado de secciones del archivo Web.config.
Introducción
La información de configuración de las aplicaciones de ASP.NET se almacena normalmente en un archivo XML denominado Web.config
. A lo largo de estos tutoriales, hemos actualizado el Web.config
varias veces. Al crear el conjunto de datos con tipo Northwind
en el primer tutorial, por ejemplo, la información de la cadena de conexión se agregó automáticamente a Web.config
en la sección <connectionStrings>
. Más adelante, en el tutorial Páginas maestras y navegación del sitio, actualizamos Web.config
manualmente, agregando un elemento <pages>
que indica que todas las páginas de ASP.NET del proyecto deben usar el tema DataWebControls
.
Dado que Web.config
puede contener datos confidenciales, como cadenas de conexión, es importante que el contenido de Web.config
se mantenga protegido y oculto de los espectadores no autorizados. De forma predeterminada, el motor de ASP.NET controla cualquier solicitud HTTP a un archivo con la extensión .config
, que devuelve el mensaje Este tipo de página no se sirve que se muestra en la Figura 1. Esto significa que los visitantes no pueden ver el contenido del archivo Web.config
simplemente escribiendo http://www.YourServer.com/Web.config en su barra de direcciones del explorador.
Figura 1: visitar Web.config
a través de un explorador devuelve un mensaje de Este tipo de página no se sirve (haga clic para ver la imagen a tamaño completo)
Pero, ¿qué ocurre si un atacante puede encontrar alguna otra vulnerabilidad que le permita ver el contenido del archivo Web.config
? ¿Qué podría hacer un atacante con esta información y qué pasos se pueden realizar para proteger aún más la información confidencial dentro de Web.config
? Afortunadamente, la mayoría de las secciones de Web.config
no contienen información confidencial. ¿Qué daño puede perpetrar un atacante si conoce el nombre del tema predeterminado usado por las páginas ASP.NET?
Sin embargo, algunas secciones de Web.config
contienen información confidencial que puede incluir cadenas de conexión, nombres de usuario, contraseñas, nombres de servidor, claves de cifrado, etc. Esta información se encuentra normalmente en las siguientes secciones Web.config
:
<appSettings>
<connectionStrings>
<identity>
<sessionState>
En este tutorial veremos técnicas para proteger dicha información de configuración confidencial. Como veremos, la versión 2.0 de .NET Framework incluye un sistema de configuraciones protegidas que hace que las secciones de configuración seleccionadas se cifren y descifren mediante programación.
Nota:
En este tutorial se concluye con un vistazo a las recomendaciones de Microsoft para conectarse a una base de datos desde una aplicación de ASP.NET. Además de cifrar las cadenas de conexión, puede ayudar a proteger el sistema asegurándose de que se conecta a la base de datos de forma segura.
Paso 1: explorar las opciones de configuración protegidas de ASP.NET 2.0
ASP.NET 2.0 incluye un sistema de configuración protegido para cifrar y descifrar información de configuración. Esto incluye métodos de .NET Framework que se pueden usar para cifrar o descifrar la información de configuración mediante programación. El sistema de configuración protegido usa el modelo de proveedor que permite a los desarrolladores elegir qué implementación criptográfica se usa.
.NET Framework se incluye con dos proveedores de configuración protegidos:
RSAProtectedConfigurationProvider
: usa el algoritmo RSA asimétrico para el cifrado y el descifrado.DPAPIProtectedConfigurationProvider
: usa la API de protección de datos (DPAPI) de Windows para el cifrado y el descifrado.
Dado que el sistema de configuración protegido implementa el patrón de diseño del proveedor, es posible crear su propio proveedor de configuración protegido y conectarlo a la aplicación. Consulte Implementar un proveedor de configuración protegido para obtener más información sobre este proceso.
Los proveedores RSA y DPAPI usan claves para sus rutinas de cifrado y descifrado, y estas claves se pueden almacenar en el nivel de equipo o usuario. Las claves de nivel de máquina son ideales para escenarios en los que la aplicación web se ejecuta en su propio servidor dedicado o si hay varias aplicaciones en un servidor que necesitan compartir información cifrada. Las claves de nivel de usuario son una opción más segura en entornos de hospedaje compartidos en los que otras aplicaciones del mismo servidor no deben poder descifrar las secciones de configuración protegida de la aplicación.
En este tutorial, nuestros ejemplos usarán el proveedor de DPAPI y las claves de nivel de máquina. En concreto, veremos el cifrado de la sección <connectionStrings>
en Web.config
, aunque el sistema de configuración protegido se puede usar para cifrar la mayoría de las secciones de Web.config
. Para obtener información sobre el uso de claves de nivel de usuario o el uso del proveedor RSA, consulte los recursos de la sección Lecturas adicionales al final de este tutorial.
Nota:
Los proveedores de RSAProtectedConfigurationProvider
y DPAPIProtectedConfigurationProvider
se registran en el archivo machine.config
con los nombres de proveedor RsaProtectedConfigurationProvider
y DataProtectionConfigurationProvider
, respectivamente. Al cifrar o descifrar la información de configuración, necesitaremos proporcionar el nombre de proveedor adecuado (RsaProtectedConfigurationProvider
o DataProtectionConfigurationProvider
) en lugar del nombre de tipo real (RSAProtectedConfigurationProvider
y DPAPIProtectedConfigurationProvider
). Puede encontrar el archivo machine.config
en la carpeta $WINDOWS$\Microsoft.NET\Framework\version\CONFIG
.
Paso 2: secciones de configuración de cifrado y descifrado mediante programación
Con algunas líneas de código podemos cifrar o descifrar una sección de configuración determinada mediante un proveedor especificado. El código, como veremos en breve, simplemente necesita hacer referencia mediante programación a la sección de configuración adecuada, llamar a su método ProtectSection
o UnprotectSection
y, a continuación, llamar al método Save
para conservar los cambios. Además, .NET Framework incluye una utilidad de línea de comandos útil que puede cifrar y descifrar la información de configuración. Exploraremos esta utilidad de línea de comandos en el paso 3.
Para ilustrar mediante programación la protección de la información de configuración, vamos a crear una página ASP.NET que incluya botones para cifrar y descifrar la sección <connectionStrings>
en Web.config
.
Para empezar, abra la página EncryptingConfigSections.aspx
en la carpeta AdvancedDAL
. Arrastre un control de Cuadro de texto desde el Cuadro de herramientas hasta el Diseñador, estableciendo su propiedad ID
en WebConfigContents
, su propiedad TextMode
en MultiLine
, y sus propiedades Width
y Rows
en 95 % y 15, respectivamente. Este control de Cuadro de texto mostrará el contenido de Web.config
que nos permite ver rápidamente si el contenido está cifrado o no. Por supuesto, en una aplicación real nunca querrá mostrar el contenido de Web.config
.
Debajo del Cuadro de texto, agregue dos controles de botón denominados EncryptConnStrings
y DecryptConnStrings
. Establezca sus propiedades Texto en Cifrar cadenas de conexión y Descifrar cadenas de conexión.
En este momento, la pantalla debe ser similar a la Figura 2.
Figura 2: agregar un Cuadro de texto y dos controles web de botón a la página (haga clic para ver la imagen a tamaño completo)
A continuación, es necesario escribir código que cargue y muestre el contenido de Web.config
en el Cuadro de texto de WebConfigContents
cuando se cargue la página por primera vez. Agregue el código siguiente a la clase de código subyacente de la página. Este código agrega un método denominado DisplayWebConfig
y lo llama desde el controlador de eventos Page_Load
cuando Page.IsPostBack
sea false
:
protected void Page_Load(object sender, EventArgs e)
{
// On the first page visit, call DisplayWebConfig method
if (!Page.IsPostBack)
DisplayWebConfig();
}
private void DisplayWebConfig()
{
// Reads in the contents of Web.config and displays them in the TextBox
StreamReader webConfigStream =
File.OpenText(Path.Combine(Request.PhysicalApplicationPath, "Web.config"));
string configContents = webConfigStream.ReadToEnd();
webConfigStream.Close();
WebConfigContents.Text = configContents;
}
El método DisplayWebConfig
usa la clase File
para abrir el archivo Web.config
de la aplicación, la clase StreamReader
para leer su contenido en una cadena y la clase Path
para generar la ruta de acceso física al archivo Web.config
. Estas tres clases se encuentran en el espacio de nombres System.IO
. Por lo tanto, deberá agregar una instrucción using
System.IO
a la parte superior de la clase de código subyacente o, como alternativa, coloque a estos nombres de clase el prefijo System.IO.
.
A continuación, es necesario agregar controladores de eventos para los dos eventos de controles de botón Click
y agregar el código necesario para cifrar y descifrar la sección <connectionStrings>
mediante una clave de nivel de máquina con el proveedor DPAPI. En el Diseñador, haga doble clic en cada uno de los botones para agregar un controlador de eventos Click
en la clase de código subyacente y agregue el código siguiente:
protected void EncryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings = config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only encrypt the section if it is not already protected
if (!connectionStrings.SectionInformation.IsProtected)
{
// Encrypt the <connectionStrings> section using the
// DataProtectionConfigurationProvider provider
connectionStrings.SectionInformation.ProtectSection(
"DataProtectionConfigurationProvider");
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
protected void DecryptConnStrings_Click(object sender, EventArgs e)
{
// Get configuration information about Web.config
Configuration config =
WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
// Let's work with the <connectionStrings> section
ConfigurationSection connectionStrings =
config.GetSection("connectionStrings");
if (connectionStrings != null)
// Only decrypt the section if it is protected
if (connectionStrings.SectionInformation.IsProtected)
{
// Decrypt the <connectionStrings> section
connectionStrings.SectionInformation.UnprotectSection();
config.Save();
// Refresh the Web.config display
DisplayWebConfig();
}
}
El código usado en los dos controladores de eventos es casi idéntico. Ambos comienzan obteniendo información sobre el archivo Web.config
de la aplicación actual a través del método OpenWebConfiguration
de la clase WebConfigurationManager
. Este método devuelve el archivo de configuración web para la ruta de acceso virtual especificada. A continuación, se accede a la sección <connectionStrings>
del archivo Web.config
a través del método GetSection(sectionName)
de la clase Configuration
, que devuelve un objeto ConfigurationSection
.
El objeto ConfigurationSection
incluye una propiedad SectionInformation
que proporciona información adicional y funcionalidad sobre la sección de configuración. Como se muestra en el código anterior, podemos determinar si la sección de configuración está cifrada comprobando la propiedad SectionInformation
de la propiedad IsProtected
. Además, la sección se puede cifrar o descifrar a través de las propiedades SectionInformation
de ProtectSection(provider)
y los métodos UnprotectSection
.
El método ProtectSection(provider)
acepta como entrada una cadena que especifica el nombre del proveedor de configuración protegido que se va a usar al cifrar. En el controlador de eventos de botón EncryptConnString
pasamos DataProtectionConfigurationProvider al método ProtectSection(provider)
para que se use el proveedor DPAPI. El método UnprotectSection
puede determinar el proveedor que se usó para cifrar la sección de configuración y, por lo tanto, no requiere ningún parámetro de entrada.
Después de llamar al método ProtectSection(provider)
o UnprotectSection
, debe llamar al método Save
del objeto Configuration
para conservar los cambios. Una vez que la información de configuración se ha cifrado o descifrado y los cambios guardados, llamamos a DisplayWebConfig
para cargar el contenido de Web.config
actualizado en el control de Cuadro de texto.
Una vez que haya escrito el código anterior, pruébelo visitando la página de EncryptingConfigSections.aspx
a través de un explorador. Debería ver inicialmente una página que muestra el contenido de Web.config
con la sección <connectionStrings>
que se muestra en texto sin formato (vea la Figura 3).
Figura 3: agregar un Cuadro de texto y dos controles web de botón a la página (haga clic para ver la imagen a tamaño completo)
Ahora haga clic en el botón Cifrar cadenas de conexión. Si la validación de solicitudes está habilitada, el marcado publicado de nuevo desde el Cuadro de texto WebConfigContents
generará un HttpRequestValidationException
, que muestra el mensaje: Se detectó un valor de Request.Form
potencialmente peligroso desde el cliente. La validación de solicitudes, que está habilitada de forma predeterminada en ASP.NET 2.0, prohíbe los postbacks que incluyen HTML sin codificar y está diseñada para ayudar a evitar ataques por inyección de scripts. Esta comprobación se puede deshabilitar en el nivel de página o aplicación. Para desactivarla para esta página, establezca el valor de ValidateRequest
en false
en la directiva @Page
. La directiva @Page
se encuentra en la parte superior del marcado declarativo de la página.
<%@ Page ValidateRequest="False" ... %>
Para obtener más información sobre la validación de solicitudes, su propósito, cómo deshabilitarla en el nivel de página y aplicación, además de cómo codificar el marcado HTML, vea Validación de solicitudes: prevención de ataques de scripts.
Después de deshabilitar la validación de solicitudes para la página, intente volver a hacer clic en el botón Cifrar cadenas de conexión. En postback, se tendrá acceso al archivo de configuración y se cifrará su sección <connectionStrings>
mediante el proveedor DPAPI. El Cuadro de texto se actualiza para mostrar el nuevo contenido Web.config
. Como se muestra en la figura 4, la información de <connectionStrings>
ahora está cifrada.
Figura 4: al hacer clic en el botón Cifrar cadenas de conexión se cifra la sección <connectionString>
(haga clic para ver la imagen a tamaño completo)
La sección <connectionStrings>
cifrada generada en mi equipo sigue mostrándose, aunque parte del contenido del elemento <CipherData>
se ha quitado por brevedad:
<connectionStrings
configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/...zChw==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
Nota:
El elemento <connectionStrings>
especifica el proveedor utilizado para realizar el cifrado (DataProtectionConfigurationProvider
). El método UnprotectSection
usa esta información cuando se hace clic en el botón Descifrar cadenas de conexión.
Cuando se accede a la información de la cadena de conexión desde Web.config
, ya sea por código que se escribe, desde un control SqlDataSource o desde el código generado automáticamente desde TableAdapters en nuestros DataSets con tipo, se descifra automáticamente. En resumen, no es necesario agregar ningún código o lógica adicional para descifrar la sección cifrada <connectionString>
. Para demostrar esto, visite uno de los tutoriales anteriores en este momento, como el tutorial de visualización simple de la sección Informes básicos (~/BasicReporting/SimpleDisplay.aspx
). Como se muestra en la figura 5, el tutorial funciona exactamente como se esperaría, lo que indica que la página de ASP.NET descifra automáticamente la información de la cadena de conexión cifrada.
Figura 5: la capa de acceso a datos descifra automáticamente la información de cadena de conexión (haga clic para ver la imagen a tamaño completo)
Para revertir la sección <connectionStrings>
a su representación de texto sin formato, haga clic en el botón Descifrar cadenas de conexión. En postback, debería ver las cadenas de conexión en Web.config
en texto sin formato. En este punto, su pantalla debería parecerse a la que tenía la primera vez que visitó esta página (véase la Figura 3).
Paso 3: cifrar secciones de configuración mediante aspnet_regiis.exe
.NET Framework incluye una variedad de herramientas de línea de comandos en la carpeta $WINDOWS$\Microsoft.NET\Framework\version\
. En el tutorial Usar dependencias de caché de SQL, por ejemplo, hemos examinado el uso de la herramienta de línea de comandos aspnet_regsql.exe
para agregar la infraestructura necesaria para las dependencias de caché de SQL. Otra herramienta de línea de comandos útil de esta carpeta es la Herramienta de Registro de IIS de ASP.NET (aspnet_regiis.exe
). Como su nombre implica, la herramienta de Registro de IIS de ASP.NET se usa principalmente para registrar una aplicación de ASP.NET 2.0 con el servidor web de nivel profesional de Microsoft, IIS. Además de sus características relacionadas con IIS, la herramienta de registro de IIS de ASP.NET también se puede usar para cifrar o descifrar secciones de configuración especificadas en Web.config
.
En la siguiente instrucción se muestra la sintaxis general que se usa para cifrar una sección de configuración con la herramienta de línea de comandos aspnet_regiis.exe
:
aspnet_regiis.exe -pef section physical_directory -prov provider
section es la sección de configuración para cifrar (como connectionStrings ), physical_directory es la ruta de acceso física completa al directorio raíz de la aplicación web y provider es el nombre del proveedor de configuración protegido que se va a usar (como DataProtectionConfigurationProvider ). Como alternativa, si la aplicación web está registrada en IIS, puede escribir la ruta de acceso virtual en lugar de la ruta de acceso física mediante la siguiente sintaxis:
aspnet_regiis.exe -pe section -app virtual_directory -prov provider
En el siguiente ejemplo de aspnet_regiis.exe
se cifra la sección <connectionStrings>
mediante el proveedor DPAPI con una clave de nivel de máquina:
aspnet_regiis.exe -pef
"connectionStrings" "C:\Websites\ASPNET_Data_Tutorial_73_CS"
-prov "DataProtectionConfigurationProvider"
Del mismo modo, la herramienta de línea de comandos aspnet_regiis.exe
se puede usar para descifrar secciones de configuración. En lugar de usar el modificador -pef
, use -pdf
(o en lugar de -pe
, use -pd
). Además, tenga en cuenta que el nombre del proveedor no es necesario al descifrar.
aspnet_regiis.exe -pdf section physical_directory
-- or --
aspnet_regiis.exe -pd section -app virtual_directory
Nota:
Puesto que usamos el proveedor DPAPI, que usa claves específicas del equipo, debe ejecutar aspnet_regiis.exe
desde la misma máquina desde la que se sirven las páginas web. Por ejemplo, si ejecuta este programa de línea de comandos desde el equipo de desarrollo local y, a continuación, carga el archivo Web.config cifrado en el servidor de producción, este no podrá descifrar la información de la cadena de conexión, ya que se cifró mediante claves específicas de la máquina de desarrollo. El proveedor RSA no tiene esta limitación, ya que es posible exportar las claves RSA a otra máquina.
Descripción de las opciones de autenticación de base de datos
Antes de que cualquier aplicación pueda emitir consultas SELECT
, INSERT
, UPDATE
o DELETE
a una base de datos de Microsoft SQL Server, la base de datos primero debe identificar al solicitante. Este proceso se conoce como autenticación y SQL Server proporciona dos métodos para ello:
- Autenticación de Windows: el proceso en el que se ejecuta la aplicación se usa para comunicarse con la base de datos. Al ejecutar una aplicación de ASP.NET a través de Visual Studio 2005 s ASP.NET Development Server, la aplicación ASP.NET asume la identidad del usuario que ha iniciado sesión actualmente. Para aplicaciones de ASP.NET en Microsoft Internet Information Server (IIS), las aplicaciones de ASP.NET suelen asumir la identidad de
domainName``\MachineName
odomainName``\NETWORK SERVICE
, aunque esto se puede personalizar. - Autenticación de SQL: se proporcionan un identificador de usuario y valores de contraseña como credenciales para la autenticación. Con la autenticación de SQL, el identificador de usuario y la contraseña se proporcionan en la cadena de conexión.
La autenticación de Windows se prefiere sobre la autenticación de SQL porque es más segura. Con la autenticación de Windows, la cadena de conexión está libre de un nombre de usuario y una contraseña y, si el servidor web y el servidor de bases de datos residen en dos máquinas diferentes, las credenciales no se envían a través de la red en texto sin formato. Por su parte, con la autenticación de SQL, las credenciales de autenticación se codifican de forma rígida en la cadena de conexión y se transmiten desde el servidor web al servidor de base de datos en texto sin formato.
Estos tutoriales han usado la autenticación de Windows. Puede indicar qué modo de autenticación se usa inspeccionando la cadena de conexión. La cadena de conexión de Web.config
para nuestros tutoriales ha sido:
Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\NORTHWND.MDF; Integrated Security=True; User Instance=True
La Integrated Security=True y la falta de un nombre de usuario y una contraseña indican que se está usando la autenticación de Windows. En algunas cadenas de conexión, el término Trusted Connection=Yes o Integrated Security=SSPI se usa en lugar de Integrated Security=True, pero los tres indican el uso de la autenticación de Windows.
En el ejemplo siguiente se muestra una cadena de conexión que usa la autenticación de SQL. $CREDENTIAL_PLACEHOLDER$
es un marcador de posición para el par clave-valor de contraseña. Tenga en cuenta que las credenciales se insertan dentro de la cadena de conexión:
Server=serverName; Database=Northwind; uid=userID; $CREDENTIAL_PLACEHOLDER$
Imagine que un atacante puede ver el archivo Web.config
s de la aplicación. Si usa la autenticación de SQL para conectarse a una base de datos accesible a través de Internet, el atacante puede usar esta cadena de conexión para conectarse a la base de datos a través de SQL Management Studio o desde páginas ASP.NET en su propio sitio web. Para ayudar a mitigar esta amenaza, cifre la información de la cadena de conexión en Web.config
mediante el sistema de configuración protegido.
Nota:
Para obtener más información sobre los distintos tipos de autenticación disponibles en SQL Server, vea Crear aplicaciones ASP.NET seguras: autenticación, autorización y comunicación seguras. Para obtener más ejemplos de cadenas de conexión que ilustran las diferencias entre la sintaxis de autenticación de Windows y SQL, consulte ConnectionStrings.com.
Resumen
De forma predeterminada, no se puede acceder a los archivos con una extensión .config
en una aplicación de ASP.NET a través de un explorador. Estos tipos de archivos no se devuelven porque pueden contener información confidencial, como cadenas de conexión de base de datos, nombres de usuario y contraseñas, etc. El sistema de configuración protegido de .NET 2.0 ayuda a proteger aún más la información confidencial al permitir que se cifren las secciones de configuración especificadas. Hay dos proveedores de configuración protegidos integrados: uno que usa el algoritmo RSA y otro que usa la API de protección de datos de Windows (DPAPI).
En este tutorial hemos visto cómo cifrar y descifrar las opciones de configuración mediante el proveedor DPAPI. Esto se puede lograr mediante programación, como vimos en el paso 2, así como a través de la herramienta de línea de comandos aspnet_regiis.exe
, que se ha tratado en el paso 3. Para obtener más información sobre el uso de claves de nivel de usuario o el uso del proveedor RSA en su lugar, consulte los recursos de la sección Lectura adicional.
¡Feliz programación!
Lecturas adicionales
Para obtener más información sobre los temas tratados en este tutorial, consulte los siguientes recursos:
- Creación de aplicaciones de ASP.NET seguras: autenticación, autorización y comunicación segura
- Cifrado de información de configuración en aplicaciones de ASP.NET 2.0
- Cifrado de valores de
Web.config
en ASP.NET 2.0 - Cómo cifrar secciones de configuración en ASP.NET 2.0 mediante DPAPI
- Cómo cifrar secciones de configuración en ASP.NET 2.0 utilizando RSA
- API de configuración en .NET 2.0
- Protección de datos de Windows
Acerca del autor
Scott Mitchell, autor de siete libros de ASP/ASP.NET y fundador de 4GuysFromRolla.com, ha trabajado con tecnologías web de Microsoft desde 1998. Scott trabaja como consultor independiente, entrenador y escritor. Su último libro es Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Puede ponerse en contacto con él a través de mitchell@4GuysFromRolla.com. o de su blog, que se puede encontrar en http://ScottOnWriting.NET.
Agradecimientos especiales a
Esta serie de tutoriales fue revisada por muchos revisores de gran ayuda. Los revisores principales de este tutorial fueron Teresa Murphy y Randy Schmidt. ¿Le interesa revisar mis próximos artículos de MSDN? Si es así, escríbame a mitchell@4GuysFromRolla.com.