Prácticas recomendadas para implementar contraseñas y otros datos confidenciales en ASP.NET y Azure App Service

por Rick Anderson

En este tutorial se muestra cómo el código puede almacenar y acceder de forma segura a la información segura. El punto más importante es que nunca debe almacenar contraseñas u otros datos confidenciales en el código fuente y no debe usar secretos de producción en modo de desarrollo y prueba.

El código de ejemplo es una sencilla aplicación de consola WebJob y una aplicación ASP.NET MVC que necesita acceso a una contraseña de cadena de conexión de base de datos, claves seguras de Twilio, Google y SendGrid.

También se menciona la configuración local y PHP.

Trabajo con contraseñas en el entorno de desarrollo

Los tutoriales suelen mostrar datos confidenciales en el código fuente, a ser posible con la advertencia de que nunca se deben almacenar datos confidenciales en el código fuente. Por ejemplo, mi tutorial Aplicación ASP.NET MVC 5 con SMS y correo electrónico 2FA muestra lo siguiente en el archivo web.config:

</connectionStrings>
   <appSettings>
      <add key="webpages:Version" value="3.0.0.0" />
      <!-- Markup removed for clarity. -->
      
      <!-- SendGrid-->
      <add key="mailAccount" value="account" />
      <add key="mailPassword" value="my password" />
      <!-- Twilio-->
      <add key="TwilioSid" value="My SID" />
      <add key="TwilioToken" value="My Token" />
      <add key="TwilioFromPhone" value="+12065551234" />

      <add key="GoogClientID" value="1234.apps.googleusercontent.com" />
      <add key="GoogClientSecret" value="My GCS" />
   </appSettings>
 <system.web>

El archivo web.config es código fuente, por lo que estos secretos nunca deben almacenarse en ese archivo. Afortunadamente, el elemento <appSettings> tiene un atributo file que permite especificar un archivo externo que contiene la configuración de la aplicación confidencial. Puede mover todos los secretos a un archivo externo siempre que no esté protegido en el árbol de origen. Por ejemplo, en el siguiente marcado, el archivo AppSettingsSecrets.config contiene todos los secretos de la aplicación:

</connectionStrings>
   <appSettings file="..\..\AppSettingsSecrets.config">      
      <add key="webpages:Version" value="3.0.0.0" />
      <add key="webpages:Enabled" value="false" />
      <add key="ClientValidationEnabled" value="true" />
      <add key="UnobtrusiveJavaScriptEnabled" value="true" />      
   </appSettings>
  <system.web>

El marcado del archivo externo (AppSettingsSecrets.config en este ejemplo), es el mismo marcado que se encuentra en el archivo web.config:

<appSettings>   
   <!-- SendGrid-->
   <add key="mailAccount" value="My mail account." />
   <add key="mailPassword" value="My mail password." />
   <!-- Twilio-->
   <add key="TwilioSid" value="My Twilio SID." />
   <add key="TwilioToken" value="My Twilio Token." />
   <add key="TwilioFromPhone" value="+12065551234" />

   <add key="GoogClientID" value="1.apps.googleusercontent.com" />
   <add key="GoogClientSecret" value="My Google client secret." />
</appSettings>

El tiempo de ejecución de ASP.NET combina el contenido del archivo externo con el marcado en el elemento <appSettings>. El tiempo de ejecución omite el atributo de archivo si no se encuentra el archivo especificado.

Advertencia

Seguridad: no agregue el archivo secrets .config al proyecto ni lo compruebe en el control de código fuente. De forma predeterminada, Visual Studio establece el Build Action en Content, lo que significa que se implementa el archivo. Para obtener más información, consulte ¿Por qué no se implementan todos los archivos de la carpeta del proyecto? Aunque puede usar cualquier extensión para el archivo secrets.config, es mejor mantenerlo como .config, ya que IIS no atiende los archivos de configuración. Observe también que el arichivo AppSettingsSecrets.config es de dos niveles de directorio desde el archivo web.config, por lo que está completamente fuera del directorio de la solución. Al mover el archivo fuera del directorio de la solución, "git add *" no lo agregará al repositorio.

Trabajo con cadenas de conexión en el entorno de desarrollo

Visual Studio crea nuevos proyectos de ASP.NET que usan LocalDB. LocalDB se creó específicamente para el entorno de desarrollo. No requiere una contraseña, por lo que no es necesario hacer nada para evitar que los secretos se protejan en el código fuente. Algunos equipos de desarrollo usan las versiones completas de SQL Server (u otros DBMS) que requieren una contraseña.

Puede usar el atributo configSource para reemplazar todo el marcado <connectionStrings>. A diferencia del atributo <appSettings>file que combina el marcado, el atributo configSource lo reemplaza. El siguiente marcado muestra el atributo configSource en el archivo web.config:

<connectionStrings configSource="ConnectionStrings.config">
</connectionStrings>

Nota:

Si usa el atributo configSource como se muestra anteriormente para mover los cadena de conexión a un archivo externo y hacer que Visual Studio cree un sitio web, no podrá detectar que usa una base de datos y no obtendrá la opción de configurar la base de datos al publicar en Azure desde Visual Studio. Si usa el atributo configSource, puede usar PowerShell para crear e implementar el sitio web y la base de datos, o bien puede crear el sitio web y la base de datos en el portal antes de publicar.

Advertencia

Seguridad: a diferencia del archivo AppSettingsSecrets.config, el archivo cadena de conexión s externo debe estar en el mismo directorio que el archivo web.config raíz, por lo que tendrá que tomar precauciones para asegurarse de no comprobarlo en el repositorio de origen.

Nota:

Advertencia de seguridad en el archivo de secretos: un procedimiento recomendado es no usar secretos de producción en pruebas y desarrollo. El uso de contraseñas de producción en pruebas o desarrollo filtra esos secretos.

Aplicaciones de consola de WebJobs

El archivo app.config usado por una aplicación de consola no admite rutas de acceso relativas, pero sí absolutas. Puede usar una ruta de acceso absoluta para mover los secretos fuera del directorio del proyecto. El siguiente marcado muestra los secretos en el archivo C:\secrets\AppSettingsSecrets.config y datos no confidenciales en el archivo app.config.

<configuration>
  <appSettings file="C:\secrets\AppSettingsSecrets.config">
    <add key="TwitterMaxThreads" value="24" />
    <add key="StackOverflowMaxThreads" value="24" />
    <add key="MaxDaysForPurge" value="30" />
  </appSettings>
</configuration>

Implementación de secretos en Azure

Al implementar la aplicación web en Azure, el archivo AppSettingsSecrets.config no se implementará (eso es lo que desea). Para ello, puede ir al Portal de administración de Microsoft Azure y configurarlos manualmente:

  1. Vaya a https://portal.azure.com e inicie sesión con sus credenciales de Azure.
  2. Haga clic en Examinar > Web Apps y, a continuación, haga clic en el nombre de la aplicación web.
  3. Haga clic en Toda la configuración > Configuración de la aplicación.

Los valores de la configuración de la aplicación y la cadena de conexión invalidan la misma configuración en el archivo web.config. En nuestro ejemplo, no implementamos estos ajustes en Azure, pero si estas claves estuvieran en el archivo web.config, los ajustes mostrados en el portal tendrían prioridad.

Un procedimiento recomendado es seguir un flujo de trabajo de DevOps y usar Azure PowerShell (u otro marco como Chef o Puppet) para automatizar la configuración de estos valores en Azure. El siguiente script de PowerShell usa Export-CliXml para exportar los secretos cifrados al disco:

param(
  [Parameter(Mandatory=$true)] 
  [String]$Name,
  [Parameter(Mandatory=$true)]
  [String]$Password)

$credPath = $PSScriptRoot + '\' + $Name + ".credential"
$PWord = ConvertTo-SecureString –String $Password –AsPlainText -Force 
$Credential = New-Object –TypeName `
System.Management.Automation.PSCredential –ArgumentList $Name, $PWord
$Credential | Export-CliXml $credPath

En el script anterior, "Name" es el nombre de la clave secreta, como ""FB_AppSecret" o "TwitterSecret". Puede ver el archivo ".credential" creado por el script en el explorador. El siguiente fragmento de código prueba cada uno de los archivos de credenciales y establece los secretos de la aplicación web con nombre:

Function GetPW_fromCredFile { Param( [String]$CredFile )
  $Credential = GetCredsFromFile $CredFile
  $PW = $Credential.GetNetworkCredential().Password  
  # $user just for debugging.
  $user = $Credential.GetNetworkCredential().username 
  Return $PW
}	
$AppSettings = @{	
  "FB_AppSecret"     = GetPW_fromCredFile "FB_AppSecret.credential";
  "GoogClientSecret" = GetPW_fromCredFile "GoogClientSecret.credential";
  "TwitterSecret"    = GetPW_fromCredFile "TwitterSecret.credential";
}
Set-AzureWebsite -Name $WebSiteName -AppSettings $AppSettings

Advertencia

Seguridad: no incluya contraseñas ni otros secretos en el script de PowerShell, lo que anula el propósito de usar un script de PowerShell para implementar datos confidenciales. El cmdlet Get-Credential proporciona un mecanismo seguro para obtener una contraseña. El uso de una solicitud de interfaz de usuario puede evitar la pérdida de una contraseña.

Implementación de cadena de conexión de base de datos

Las cadena de conexión de base de datos se controlan de forma similar a la configuración de la aplicación. Si implementa la aplicación web desde Visual Studio, la cadena de conexión se configurará para usted. Puede comprobarlo en el portal. La manera recomendada de establecer la cadena de conexión es con PowerShell.

Notas para PHP

Dado que los pares clave-valor para la configuración de la aplicación y las cadenas de conexión se almacenan en variables de entorno en Azure App Service, los desarrolladores que usan marcos de aplicaciones web (como PHP) pueden recuperar fácilmente estos valores. Consulte la entrada de blog Stefan Schackow Sitios web de Windows Azure: cómo funcionan las cadenas de aplicación y conexión que muestra un fragmento de código PHP para leer la configuración de la aplicación y las cadenas de conexión.

Notas para los servidores locales

Si va a implementar en servidores web locales, puede ayudar a proteger los secretos mediante el cifrado de las secciones de configuración de los archivos de configuración. Como alternativa, puede usar el mismo enfoque recomendado para sitios web de Azure: mantener la configuración de desarrollo en los archivos de configuración y usar valores de variables de entorno para la configuración de producción. Sin embargo, en este caso, debe escribir código de aplicación para la funcionalidad automática en Azure Websites: recuperar la configuración de las variables de entorno y usar esos valores en lugar de la configuración del archivo de configuración, o usar la configuración del archivo de configuración cuando no se encuentran variables de entorno.

Recursos adicionales

Consulte la publicación de Stefan Schackow Sitios web de Windows Azure: funcionamiento de cadenas de aplicación y cadenas de conexión

Un agradecimiento especial a Barry Dorrans ( @blowdart ) y Carlos Farre por la revisión.