Con la llegada de la RGPD (o GDPR, General Data Protection Regulation) y sus efectos sobre todas las empresas respecto a la privacidad, nos vimos obligados a introducir modificaciones en nuestras aplicaciones web con objeto de soportar los nuevos requisitos de seguridad y privacidad impuestos por las autoridades europeas.
La entrada en vigor, el 25 de mayo de 2018, prácticamente coincidió con el lanzamiento de ASP.NET Core 2.1, que se subió al hype y aprovechó para introducir algunas novedades que, de alguna forma, nos ayudaban a implementar algunos de los requisitos exigidos por esta nueva normativa:
- Facilitar la inclusión del típico banner de aceptación de uso de cookies durante la navegación por el sitio web y trackear su consentimiento.
- Facilitar la inclusión de una página de información sobre la privacidad del sitio.
- Evitar el envío de cookies no imprescindibles al usuario si las condiciones no han sido aceptadas.
- Permitir la distinción de cookies fundamentales para el uso del sistema de otras que son prescindibles.
- En caso de usar Identity Framework, permitir la descarga y eliminación de datos del usuario.
Algunas de estas funcionalidades, que exploraremos en mayor detalle a lo largo de este artículo, se implementan en forma de componentes incluidos en el framework ASP.NET Core, mientras que otras son simples adiciones a la plantilla de proyectos MVC y Razor Pages.
Nota: este artículo asume que tienes al menos conocimientos básicos de trabajo con ASP.NET Core MVC.
Configuración de funcionalidades
Desde ASP.NET Core 2.1, los nuevos proyectos MVC y Razor Pages incluyen en la clase Startup
código específico para añadir a las aplicaciones funcionalidades relativas a la aceptación de cookies, tanto en la configuración de servicios como en la definición del pipeline:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseCookiePolicy();
...
}
}
El objeto CookiePolicyOptions
permite personalizar algunos aspectos de estas funcionalidades, como la configuración de la cookie que será utilizada para recordar la aceptación del usuario o la lambda CheckConsentNeeded
, donde podemos introducir lógica que decida si debemos exigir el consentimiento del usuario en la petición actual.
Por ejemplo, con el siguiente código indicaríamos al framework que las cookies de consentimiento serán sólo obligatorias cuando el usuario navegue por rutas comenzando por "/home":
options.CheckConsentNeeded = context =>
{
// Introducir lógica personalizada aquí...
return context.Request.Path.StartsWithSegments("/home");
};
Inclusión del banner de aceptación de cookies
De nuevo encontramos cambios en la plantilla de proyectos MVC y Razor de ASP.NET Core, en este caso para mostrar el banner de aceptación de cookies y procesar la aceptación por parte del usuario.
Para ello, se incluye de serie la vista parcial /Shared/_CookieConsentPartial.cshtml
, que se añade a todas las vistas desde el Layout de la aplicación. En esta vista parcial se maqueta el banner de aviso de uso de cookies, y se introduce un pequeño script para que el browser añada una cookie para indicar que el usuario aceptó las condiciones de uso:
El código fuente de la misma es el siguiente:
@using Microsoft.AspNetCore.Http.Features
@{
var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
var showBanner = !consentFeature?.CanTrack ?? false;
var cookieString = consentFeature?.CreateConsentCookie();
}
@if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show">
Use this space to summarize your privacy and cookie use policy.
<a asp-area="" asp-controller="Home" asp-action="Privacy">Learn More</a>.
<button type="button" class="accept-policy close"
data-dismiss="alert" aria-label="Close" data-cookie-string="@cookieString">
<span aria-hidden="true">Accept</span>
</button>
</div>
<script>
(function () {
var button = document.querySelector("#cookieConsent button[data-cookie-string]");
button.addEventListener("click", function (event) {
document.cookie = button.dataset.cookieString;
}, false);
})();
</script>
}
Lo único "raro" de este código es que desde el lado servidor se usa el contexto de la vista para obtener la instancia de la clase que utiliza ASP.NET Core para el seguimiento de la aceptación de las condiciones, ITrackingConsentFeature
.
Como puedes observar, se utiliza luego la propiedad CanTrack
para determinar si el banner debe ser mostrado o no. Esta propiedad estará establecida a true
si el consentimiento no es requerido (es decir, si la lambda CheckConsentNeeded
de la configuración retorna falso) o si existiera la cookie que indica que el usuario aceptó las condiciones de uso.
Por otra parte, se utiliza el método CreateConsentCookie()
para obtener el contenido de la cookie que el browser añadirá a la sesión de navegación cuando el usuario dé su consentimiento pulsando el botón incluido en el banner.
Usando la configuración por defecto, un ejemplo de cookie que podría generarse sería el siguiente:
.AspNet.Consent=yes; expires=Sun, 05 Apr 2020 11:39:01 GMT; path=/; secure; samesite=lax
Las propiedades de la cookie, como su nombre o duración, se pueden personalizar desde el bloque de código que hemos visto anteriormente en la configuración de servicios.
Inclusión de página de información sobre la privacidad
Las plantillas de proyecto de sitios web basados en ASP.NET Core también incluyen de serie una página o vista específica para mostrar información sobre el tratamiento de la privacidad en el sitio web, accesible desde el menú principal.
Obviamente no incluye ningún tipo de contenido útil más allá de un texto tipo "comodín", por lo que, salvo actuar como recordatorio de que debemos construirla, tampoco sirve para mucho más. Es localizarla y escribir lo que corresponda.
Gestión de cookies no esenciales
Las funcionalidades que hemos descrito hasta el momento están enfocadas a mostrar información sobre la privacidad del sitio y a registrar el codiciado consentimiento explícito del usuario a la utilización de cookies en nuestro sitio web.
Sin embargo, ¿cómo afecta esto de forma interna?
Pues bien, aquí es donde encontramos otras de las novedades interesantes incluidas en ASP.NET Core: el framework no permitirá el envío al cliente de cookies no esenciales mientras no tengamos el consentimiento explícito del usuario*. Siempre, claro está, en las peticiones sobre las que hayamos indicado que es obligatorio dicho consentimiento.
Es decir, si el usuario no ha aceptado la política de privacidad y uso de cookies, un código como el siguiente no enviará la cookie al lado cliente:
public IActionResult Index()
{
var visitsCookieValue = Request.Cookies["visits"]?.ToString() ?? "0";
int.TryParse(visitsCookieValue, out int visits);
visits++;
Response.Cookies.Append("visits", visits.ToString());
...
return View();
}
Esto es así porque, por defecto, las cookies que añadamos a la respuesta son consideradas como no esenciales para el funcionamiento de nuestra aplicación. El caso anterior sería un buen ejemplo de este tipo de cookies: simplemente la usamos para contar las visitas a la vista, algo que probablemente no sea del todo imprescindible para el correcto funcionamiento del sistema.
Sin embargo, si hacemos lo siguiente, estaremos indicando expresamente que la cookie es imprescindible y debe ser enviada aunque el usuario no lo haya aceptado:
public IActionResult Login(string id, string secret)
{
var token = _tokenServices.GetToken(appId, secret);
var cookieOptions = new CookieOptions()
{
IsEssential = true
};
Response.Cookies.Append("auth-token", token, cookieOptions);
...
}
Observa que, a diferencia del caso anterior, en esta ocasión la cookie sí sería esencial para el buen funcionamiento de una aplicación que utilice el token, por ejemplo, para autenticación de usuarios.
Hay que tener cuidado con esto, porque hay características de ASP.NET Core, como el mantenimiento de estado de sesión (Session
) o el almacén temporal de datos (TempData
), que utilizan cookies que por defecto no están configuradas como fundamentales.
Para modificar esto, podríamos realizar los siguientes cambios en la configuración de sus respectivos servicios:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookieTempDataProviderOptions>(options => {
options.Cookie.IsEssential = true;
});
services.AddSession(options =>
{
options.Cookie.IsEssential = true;
});
...
}
Hay que tener cuidado con esto porque a lo mejor no te está funcionando la sesión y es debido ¡a la configuración relacionada con la RGPD!
Descarga de información y eliminación de datos del usuario
Cuando creamos un proyecto utilizando las plantillas con autenticación local basadas en ASP.NET Core Identity, veremos también que en las páginas de gestión de la cuenta de usuario aparecerán opciones para descargar en formato JSON todos los datos almacenados del usuario, así como para eliminarlos por completo, facilitándonos el cumplimiento de los derechos de acceso:
Recapitulando...
Para asegurar el cumplimiento de la RGPD en nuestras aplicaciones, debemos desarrollar teniendo en cuenta bastantes aspectos relativos a la seguridad y privacidad de los usuarios.
En este post hemos visto que ASP.NET Core incluye de serie ayudas interesantes para facilitarnos la adopción de algunas de las medidas impuestas por esta normativa europea. Aunque aún quedará mucho trabajo por hacer, al menos tendremos las bases sobre las que construir aplicaciones más seguras y respetuosas con la privacidad de nuestros usuarios.