Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Por Rick Anderson
El scripting entre sitios (XSS) es una vulnerabilidad que permite a un ciberdelincuente colocar scripts en el lado cliente (normalmente a través de JavaScript) en páginas web. Cuando otros usuarios cargan páginas afectadas, se ejecutan los scripts de cyberattacker. El ciberatacante puede robar cookies y tokens de sesión, cambiar el contenido de la página web mediante la manipulación del DOM o redirigir el navegador a otra página. Las vulnerabilidades XSS suelen producirse cuando una aplicación toma la entrada del usuario y la envía a una página sin validar, codificar ni escapar.
Este artículo se aplica principalmente a ASP.NET Core MVC con vistas, Razor Pages y otras aplicaciones que devuelven HTML que pueden ser vulnerables a XSS. Las API web que devuelven datos en forma de HTML, XML o JSON pueden desencadenar ataques XSS en sus aplicaciones cliente si no sanan correctamente la entrada del usuario. Este comportamiento depende de la confianza que coloca la aplicación cliente en la API. Si una API acepta contenido generado por el usuario y lo devuelve en una respuesta HTML, los datos están abiertos a ataques. Un ciberattacker puede insertar scripts malintencionados en el contenido que se ejecuta cuando la respuesta se representa en el explorador del usuario.
Para evitar ataques XSS, las API web deben implementar la validación de entrada y la codificación de salida. La validación de entrada garantiza que la entrada del usuario cumpla los criterios esperados y no incluya código malintencionado. La codificación de salida garantiza que los datos devueltos por la API estén correctamente saneados para que el explorador del usuario no pueda ejecutarlos como código. Para obtener más información, vea GitHub dotnet/aspnetcore.docs issue #28789.
Protección de la aplicación contra XSS
A nivel básico, XSS engaña a la aplicación para que inserte una etiqueta <script> en la página representada o un evento On* en un elemento.
Para evitar introducir XSS en la aplicación, los desarrolladores deben implementar las siguientes técnicas de prevención:
Nunca coloque datos que no sean de confianza en la entrada HTML, a menos que siga las otras técnicas enumeradas en esta sección.
Los datos no confiables son cualquier dato que un ciberatacante puede controlar. Algunos ejemplos incluyen entradas de formulario HTML, cadenas de consulta, encabezados HTTP o incluso datos procedentes de una base de datos. Un ciberattacker podría ser capaz de infringir la base de datos incluso si no pueden infringir la aplicación.
Antes de colocar datos que no son de confianza en un elemento HTML, asegúrese de que los datos están codificados en HTML.
La codificación HTML toma caracteres como el corchete angular izquierdo o menor que (
<) y los cambia en un formato seguro como (<).Antes de colocar datos que no son de confianza en un atributo HTML, asegúrese de que los datos están codificados con atributos HTML.
Esta forma especializada de codificación HTML controla comillas dobles (
"), comillas simples ('), ampersand (&) y menos de (<). Cuando se trabaja con una entrada no confiable, use la codificación HTML para el contenido HTML general y la codificación de atributos HTML para los atributos HTML.Antes de colocar datos que no son de confianza en JavaScript, coloque los datos en un elemento HTML cuyo contenido recupere en tiempo de ejecución.
Si no puede seguir esta técnica, asegúrese de que los datos están codificados en JavaScript. La codificación de JavaScript convierte caracteres peligrosos para JavaScript en un valor equivalente hexadecimal. Por ejemplo, la codificación de JavaScript convierte el carácter «menor que» (
<) en el valor hexadecimal\u003C.Antes de colocar datos que no son de confianza en una cadena de consulta url, asegúrese de que los datos están codificados en dirección URL.
Exploración de la codificación HTML con Razor
El Razor motor usado en MVC codifica automáticamente todas las salidas procedentes de variables, a menos que trabaje para evitar este comportamiento. Usa reglas de codificación de atributos HTML cada vez que se usa la directiva at symbol @ . Dado que la codificación de atributos HTML es un superconjunto de codificación HTML, no es necesario tener en cuenta si se debe usar la codificación HTML o la codificación de atributos HTML. Debe asegurarse de que solo use en el símbolo @ en un contexto HTML y no al intentar insertar entradas que no son de confianza directamente en JavaScript.
Razor Los asistentes de etiquetas también codifican la entrada que se usa en los parámetros de etiqueta.
Tenga en cuenta la siguiente Razor vista:
@{
var untrustedInput = "<\"123\">";
}
@untrustedInput
Esta vista genera el contenido de la untrustedInput variable. La variable incluye algunos caracteres usados en ataques XSS: menor que (<), comillas dobles (") y corchete angular derecho o mayor que (>). Al examinar el origen se muestra la salida representada codificada como:
<"123">
Advertencia
ASP.NET Core MVC proporciona una clase HtmlString que no se codifica automáticamente tras la salida. Esta clase nunca se debe usar en combinación con una entrada que no es de confianza porque expone una vulnerabilidad XSS.
Exploración de la codificación de JavaScript con Razor
En algunos casos, puede que quiera insertar un valor en JavaScript para procesarlo en su vista. Hay dos maneras de realizar esta tarea. La manera más segura de insertar valores es colocar el valor en un atributo de datos de una etiqueta y recuperarlo en JavaScript. Por ejemplo:
@{
var untrustedInput = "<script>alert(1)</script>";
}
<div id="injectedData"
data-untrustedinput="@untrustedInput" />
<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />
<script>
var injectedData = document.getElementById("injectedData");
// All clients
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
// HTML 5 clients only
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
// Put the injected, untrusted data into the scriptedWrite div tag.
// Do NOT use document.write() on dynamically generated data as it can lead to XSS.
document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;
// Or, you can use createElement() to dynamically create document elements.
// This instance uses textContent to ensure the data is properly encoded.
var x = document.createElement("div");
x.textContent = clientSideUntrustedInputHtml5;
document.body.appendChild(x);
// You can also use createTextNode on an element to ensure data is properly encoded.
var y = document.createElement("div");
y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
document.body.appendChild(y);
</script>
El marcado anterior genera el siguiente código HTML:
<div id="injectedData"
data-untrustedinput="<script>alert(1)</script>" />
<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />
<script>
var injectedData = document.getElementById("injectedData");
// All clients
var clientSideUntrustedInputOldStyle =
injectedData.getAttribute("data-untrustedinput");
// HTML 5 clients only
var clientSideUntrustedInputHtml5 =
injectedData.dataset.untrustedinput;
// Put the injected, untrusted data into the scriptedWrite div tag.
// Do NOT use document.write() on dynamically generated data as it can lead to XSS.
document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;
// Or, you can use createElement() to dynamically create document elements.
// This instance uses textContent to ensure the data is properly encoded.
var x = document.createElement("div");
x.textContent = clientSideUntrustedInputHtml5;
document.body.appendChild(x);
// You can also use createTextNode on an element to ensure data is properly encoded.
var y = document.createElement("div");
y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
document.body.appendChild(y);
</script>
El código anterior genera la siguiente salida:
<script>alert(1)</script>
<script>alert(1)</script>
<script>alert(1)</script>
Advertencia
No concatene datos no confiables en JavaScript para crear elementos del DOM ni use document.write() en contenido generado dinámicamente.
En su lugar, use uno de los métodos siguientes para evitar que el código se exponga a XSS basado en DOM:
- Llame
createElement()a y asigne valores de propiedad con los métodos o propiedades adecuados, comonode.textContent=onode.InnerText=. - Llame al método
document.CreateTextNode()y añádalo en el lugar adecuado del DOM. - Llame al método
element.SetAttribute(). - Utilice la asignación
element[attribute]=.
Acceso a codificadores en el código
Puede usar codificadores HTML, JavaScript y URL de dos maneras:
- Inyéctelos a través de la inserción de dependencias.
- Use los codificadores predeterminados contenidos en el espacio de nombres
System.Text.Encodings.Web.
Cuando se usan los codificadores predeterminados, las personalizaciones aplicadas a los intervalos de caracteres (por lo que se tratan como seguras) no surten efecto. Los codificadores predeterminados usan las reglas de codificación más seguras posibles.
Para usar los codificadores configurables mediante inyección de dependencias, los constructores deben aceptar un parámetro HtmlEncoder, JavaScriptEncoder y UrlEncoder, según corresponda.
Por ejemplo:
public class HomeController : Controller
{
HtmlEncoder _htmlEncoder;
JavaScriptEncoder _javaScriptEncoder;
UrlEncoder _urlEncoder;
public HomeController(HtmlEncoder htmlEncoder,
JavaScriptEncoder javascriptEncoder,
UrlEncoder urlEncoder)
{
_htmlEncoder = htmlEncoder;
_javaScriptEncoder = javascriptEncoder;
_urlEncoder = urlEncoder;
}
}
Codificación de parámetros de dirección URL
Si desea compilar una cadena de consulta de dirección URL con una entrada que no es de confianza como valor, use el UrlEncoder parámetro para codificar el valor:
var example = "\"Quoted Value with spaces and &\"";
var encodedValue = _urlEncoder.Encode(example);
Después de la codificación, la encodedValue variable contiene la cadena %22Quoted%20Value%20with%20spaces%20and%20%26%22. Los espacios, las comillas, la puntuación y otros caracteres no seguros se codifican por porcentaje en su valor hexadecimal. Por ejemplo, un carácter de espacio se convierte en %20.
Advertencia
No use una entrada que no sea de confianza como parte de una ruta de acceso URL. Pase siempre una entrada que no sea de confianza como un valor de cadena de consulta.
Personalización de los codificadores
De forma predeterminada, los codificadores usan una lista segura limitada al rango Unicode latino básico. Todos los caracteres fuera del intervalo indicado se codifican como equivalentes de código de caracteres. Este comportamiento también afecta a la representación mediante Razor asistentes de etiquetas y asistentes HTML porque usan los codificadores para generar las cadenas.
El propósito de este comportamiento es protegerse frente a errores desconocidos o futuros del explorador. Errores anteriores del navegador dificultaban el análisis sintáctico al procesar caracteres no ingleses. Si el sitio web usa caracteres no latinos, como chino, cirílico u otros, este comportamiento probablemente no es adecuado para su configuración.
Puede personalizar las listas seguras del codificador para incluir intervalos Unicode adecuados para la aplicación durante el inicio. Realice las personalizaciones en el archivo Program.cs .
Por ejemplo, puede usar la configuración predeterminada con un Razor asistente HTML similar al código HTML siguiente:
<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>
El marcado anterior se representa con texto chino codificado:
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Para ampliar el intervalo de caracteres tratados como seguros por el codificador, inserte la siguiente línea en el archivo Program.cs :
builder.Services.AddSingleton<HtmlEncoder>(
HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
UnicodeRanges.CjkUnifiedIdeographs }));
Puede personalizar las listas seguras del codificador para incluir intervalos Unicode adecuados para la aplicación durante el inicio, en ConfigureServices().
Por ejemplo, con la configuración predeterminada, podría usar Razor HtmlHelper así;
<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>
Cuando vea el origen de la página web, comprobará que se ha representado de la siguiente manera con el texto chino codificado;
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Para ampliar los caracteres tratados como seguros por el codificador, insertaría la siguiente línea en el método ConfigureServices() en startup.cs;
services.AddSingleton<HtmlEncoder>(
HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
UnicodeRanges.CjkUnifiedIdeographs }));
En este ejemplo se amplía la lista segura para incluir los ideógrafos unificados del rango Unicode CJK. La siguiente salida muestra la vista renderizada para el rango más amplio de caracteres seguros:
<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>
Los intervalos de lista seguros se especifican como gráficos de códigos Unicode, no como idiomas. El estándar Unicode tiene una lista de gráficos de código que puede usar para buscar el gráfico que contiene los caracteres. Cada codificador (HTML, JavaScript, URL) debe configurarse por separado.
Nota
La personalización de la lista segura solo afecta a los codificadores generados a través de la inserción de dependencias.
Si se accede directamente a un codificador mediante System.Text.Encodings.Web.*Encoder.Default, solo se usa la lista segura predeterminada, latín básico.
Determinar cuándo y dónde codificar
En general, la práctica aceptada es que la codificación tiene lugar en el punto de salida y los valores codificados nunca se deben almacenar en una base de datos.
La codificación en el punto de salida permite cambiar el uso de datos. Por ejemplo, cambie de HTML a un valor de cadena de consulta. Este enfoque le permite buscar fácilmente los datos sin tener que codificar valores antes de realizar búsquedas. También le permite aprovechar los cambios o correcciones de errores realizados en los codificadores.
Use la validación como técnica para prevenir ataques XSS
La validación puede ser una herramienta útil para limitar los ataques XSS. Por ejemplo, una cadena numérica que contiene solo los caracteres 0-9 no desencadena un ataque XSS.
La validación es más complicada cuando se acepta HTML en la entrada del usuario. El análisis de la entrada HTML puede ser difícil y, a veces, imposible. El marcado, junto con un analizador que quite el HTML incrustado, es una opción más segura para aceptar entradas enriquecidas.
Nunca confíe solo en la validación. Codifique siempre una entrada que no sea de confianza antes de la salida, independientemente de qué validación o saneamiento se realice.