Prevención de ataques de falsificación de solicitudes entre sitios (CSRF) en ASP.NET aplicación MVC
La falsificación de solicitud entre sitios (CSRF) es un ataque en el que un sitio malintencionado envía una solicitud a un sitio vulnerable donde el usuario ha iniciado sesión actualmente
Este es un ejemplo de un ataque CSRF:
Un usuario inicia sesión en
www.example.com
mediante la autenticación de formularios.El servidor autentica al usuario. La respuesta del servidor incluye una cookie de autenticación.
Sin cerrar sesión, el usuario visita un sitio web malintencionado. Este sitio malintencionado contiene el siguiente formato HTML:
<h1>You Are a Winner!</h1> <form action="http://example.com/api/account" method="post"> <input type="hidden" name="Transaction" value="withdraw" /> <input type="hidden" name="Amount" value="1000000" /> <input type="submit" value="Click Me"/> </form>
Observe que la acción de formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.
El usuario hace clic en el botón Enviar. El explorador incluye la cookie de autenticación con la solicitud.
La solicitud se ejecuta en el servidor con el contexto de autenticación del usuario y puede hacer cualquier cosa que pueda hacer un usuario autenticado.
Aunque en este ejemplo se requiere que el usuario haga clic en el botón del formulario, la página malintencionada podría ejecutar un script que envíe el formulario automáticamente. Además, el uso de SSL no impide un ataque CSRF, ya que el sitio malintencionado puede enviar una solicitud de "https://".
Normalmente, los ataques CSRF son posibles contra sitios web que usan cookies para la autenticación, ya que los exploradores envían todas las cookies pertinentes al sitio web de destino. Sin embargo, los ataques CSRF no se limitan a aprovechar las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Después de que un usuario inicie sesión con la autenticación básica o implícita. el explorador envía automáticamente las credenciales hasta que finaliza la sesión.
Tokens antifalsificación
Para ayudar a evitar ataques CSRF, ASP.NET MVC usa tokens antifalsificación, también denominados tokens de comprobación de solicitudes.
- El cliente solicita una página HTML que contiene un formulario.
- El servidor incluye dos tokens en la respuesta. Un token se envía como una cookie. El otro se coloca en un campo de formulario oculto. Los tokens se generan aleatoriamente para que un adversario no pueda adivinar los valores.
- Cuando el cliente envía el formulario, debe devolver ambos tokens al servidor. El cliente envía el token de cookie como una cookie y envía el token de formulario dentro de los datos del formulario. (Un cliente del explorador lo hace automáticamente cuando el usuario envía el formulario).
- Si una solicitud no incluye ambos tokens, el servidor no permite la solicitud.
Este es un ejemplo de un formulario HTML con un token de formulario oculto:
<form action="/Home/Test" method="post">
<input name="__RequestVerificationToken" type="hidden"
value="6fGBtLZmVBZ59oUad1Fr33BuPxANKY9q3Srr5y[...]" />
<input type="submit" value="Submit" />
</form>
Los tokens antifalsificación funcionan porque la página malintencionada no puede leer los tokens del usuario, debido a directivas de mismo origen. (Las directivas de mismo origen impiden que los documentos hospedados en dos sitios diferentes accedan al contenido del otro. Por lo tanto, en el ejemplo anterior, la página malintencionada puede enviar solicitudes a example.com, pero no puede leer la respuesta).
Para evitar ataques CSRF, use tokens antifalsificación con cualquier protocolo de autenticación en el que el explorador envíe credenciales silenciosamente después de que el usuario inicie sesión. Esto incluye protocolos de autenticación basados en cookies, como la autenticación de formularios, así como protocolos como la autenticación básica y implícita.
Debe requerir tokens antifalsificación para cualquier método no seguro (POST, PUT, DELETE). Además, asegúrese de que los métodos seguros (GET, HEAD) no tienen ningún efecto secundario. Además, si habilita la compatibilidad entre dominios, como CORS o JSONP, incluso los métodos seguros como GET son potencialmente vulnerables a los ataques CSRF, lo que permite al atacante leer datos potencialmente confidenciales.
Tokens antifalsificación en ASP.NET MVC
Para agregar los tokens antifalsificación a una página de Razor, use el método auxiliar HtmlHelper.AntiForgeryToken:
@using (Html.BeginForm("Manage", "Account")) {
@Html.AntiForgeryToken()
}
Este método agrega el campo de formulario oculto y también establece el token de cookie.
Anti-CSRF y AJAX
el token de formulario puede ser un problema para las solicitudes AJAX, porque estas podrían enviar datos JSON en lugar de datos de formulario HTML. Una solución consiste en enviar los tokens en un encabezado HTTP personalizado. En el código siguiente, se utiliza sintaxis de Razor para generar los tokens, que después se agregan a una solicitud AJAX. Los tokens se generan en el servidor llamando a AntiForgery.GetTokens.
<script>
@functions{
public string TokenHeaderValue()
{
string cookieToken, formToken;
AntiForgery.GetTokens(null, out cookieToken, out formToken);
return cookieToken + ":" + formToken;
}
}
$.ajax("api/values", {
type: "post",
contentType: "application/json",
data: { }, // JSON data goes here
dataType: "json",
headers: {
'RequestVerificationToken': '@TokenHeaderValue()'
}
});
</script>
Cuando procese la solicitud, extraiga los tokens del encabezado de la solicitud. A continuación, llame al método AntiForgery.Validate para validar los tokens. El método Validate produce una excepción si los tokens no son válidos.
void ValidateRequestHeader(HttpRequestMessage request)
{
string cookieToken = "";
string formToken = "";
IEnumerable<string> tokenHeaders;
if (request.Headers.TryGetValues("RequestVerificationToken", out tokenHeaders))
{
string[] tokens = tokenHeaders.First().Split(':');
if (tokens.Length == 2)
{
cookieToken = tokens[0].Trim();
formToken = tokens[1].Trim();
}
}
AntiForgery.Validate(cookieToken, formToken);
}