码迷,mamicode.com
首页 > Web开发 > 详细

防止ASP.NET Core中的跨站点请求伪造(XSRF / CSRF)攻击

时间:2019-01-18 20:06:03      阅读:949      评论:0      收藏:0      [点我收藏+]

标签:sub   builder   变量   error   css   cat   方案   tom   登录   

跨站点请求伪造(也称为XSRF或CSRF,发音为see-surf)是对Web托管应用程序的攻击,其中恶意Web应用程序可以影响客户端浏览器与信任该浏览器的Web应用程序之间的交互。这些攻击是可能的,因为Web浏览器会在每次向网站发出请求时自动发送某些类型的身份验证令牌。这种漏洞利用形式也称为一键式攻击会话,因为攻击利用了用户以前经过身份验证的会话。

CSRF攻击的一个例子:

  1. 用户www.good-banking-site.com登录使用表单身份验证。服务器对用户进行身份验证并发出包含身份验证cookie的响应。该站点容易受到攻击,因为它信任它使用有效身份验证cookie接收的任何请求。

  2. 用户访问恶意网站www.bad-crook-site.com

    恶意网站www.bad-crook-site.com包含类似于以下内容的HTML表单:

    HTML
    <h1>Congratulations! You‘re a Winner!</h1>
    <form action="http://good-banking-site.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw">
        <input type="hidden" name="Amount" value="1000000">
        <input type="submit" value="Click to collect your prize!">
    </form>
    

    请注意,表单的action帖子是易受攻击的站点,而不是恶意站点。这是CSRF的“跨站点”部分。

  3. 用户选择提交按钮。浏览器发出请求并自动包含所请求域的身份验证cookie www.good-banking-site.com

  4. 请求www.good-banking-site.com使用用户的身份验证上下文服务器上运行,并且可以执行允许经过身份验证的用户执行的任何操作。

除了用户选择提交表单的按钮的场景之外,恶意站点还可以:

  • 运行自动提交表单的脚本。
  • 将表单提交作为AJAX请求发送。
  • 使用CSS隐藏表单。

除了最初访问恶意站点之外,这些备选方案不需要用户的任何操作或输入。

使用HTTPS不会阻止CSRF攻击。恶意站点可以https://www.good-banking-site.com/像发送不安全请求一样轻松地发送请求。

某些攻击针对响应GET请求的端点,在这种情况下,可以使用图像标记来执行操作。这种形式的攻击在允许图像但阻止JavaScript的论坛网站上很常见。在GET请求中更改状态的应用程序(其中变量或资源被更改)容易受到恶意攻击。改变状态的GET请求是不安全的。最佳做法是永远不要在GET请求上更改状态。

对于使用cookie进行身份验证的Web应用程序,CSRF攻击是可能的,因为:

  • 浏览器存储由Web应用程序发布的cookie。
  • 存储的cookie包括经过身份验证的用户的会话cookie。
  • 无论在浏览器中如何生成应用程序请求,浏览器都会将与域关联的所有Cookie发送到Web应用程序。

但是,CSRF攻击不仅限于利用cookie。例如,Basic和Digest身份验证也很容易受到攻击。用户使用“基本”或“摘要”身份验证登录后,浏览器会自动发送凭据,直到会话†结束。

†在此上下文中,会话是指用户通过身份验证的客户端会话。它与服务器端会话或ASP.NET核心会话中间件无关

用户可以采取预防措施来防范CSRF漏洞:

  • 完成使用后,请注销Web应用程序。
  • 定期清除浏览器cookie。

但是,CSRF漏洞基本上是Web应用程序的问题,而不是最终用户。

身份验证基础知识

基于Cookie的身份验证是一种流行的身份验证形式。基于令牌的身份验证系统越来越受欢迎,特别是对于单页应用程序(SPA)。

当用户使用其用户名和密码进行身份验证时,会向他们发放一个令牌,其中包含可用于身份验证和授权的身份验证票证。令牌存储为cookie,伴随客户端发出的每个请求。生成和验证此cookie由Cookie身份验证中间件执行。所述中间件串行化一个用户主要成加密的cookie。在后续请求中,中间件验证cookie,重新创建主体,并将主体分配给HttpContextUser属性

基于令牌的身份验证

用户通过身份验证后,会发出令牌(而不是防伪令牌)。令牌包含声明形式的用户信息或参考令牌,用于将应用指向应用中维护的用户状态。当用户尝试访问需要身份验证的资源时,会使用Bearer令牌形式的附加授权标头将令牌发送到应用程序。这使得app无国籍。在每个后续请求中,令牌都在服务器端验证请求中传递。此令牌未加密 ; 它是编码的在服务器上,令牌被解码以访问其信息。要在后续请求中发送令牌,请将令牌存储在浏览器的本地存储中。如果令牌存储在浏览器的本地存储中,请不要担心CSRF漏洞。当令牌存储在cookie中时,CSRF是一个问题。

在一个域托管多个应用

共享托管环境容易受到会话劫持,登录CSRF和其他攻击。

虽然example1.contoso.net并且example2.contoso.net是不同的主机,但*.contoso.net下的主机之间存在隐式信任关系此隐式信任关系允许可能不受信任的主机影响彼此的cookie(管理AJAX请求的同源策略不一定适用于HTTP cookie)。

可以通过不共享域来防止在同一域上托管的应用之间利用可信cookie的攻击。当每个应用程序托管在其自己的域上时,不会使用隐式cookie信任关系。

ASP.NET Core防伪配置

 警告

ASP.NET Core使用ASP.NET Core Data Protection实现防伪必须将数据保护堆栈配置为在服务器场中工作。有关更多信息,请参阅配置数据保护

在ASP.NET Core 2.0或更高版本中,FormTagHelper将防伪标记注入HTML表单元素。Razor文件中的以下标记会自动生成防伪标记:

CSHTML
<form method="post">
    ...
</form>

类似地,如果表单的方法不是GET IHtmlHelper.BeginForm默认生成防伪标记。

<form>标记包含method="post"属性并且以下任一情况为真时,就会自动生成HTML表单元素的防伪标记

  • action属性为empty(action="")。
  • 未提供action属性(<form method="post">)。

可以禁用为HTML表单元素自动生成防伪标记:

  • 使用以下asp-antiforgery属性显式禁用防伪令牌

    CSHTML
    <form method="post" asp-antiforgery="false">
        ...
    </form>
    
  • 使用Tag Helper,表单元素选择了Tag Helpers 选择退出符号

    CSHTML
    <!form method="post">
        ...
    </!form>
    
  • FormTagHelper从视图中删除FormTagHelper可以从通过添加以下指令到剃刀图的图中移除:

    CSHTML
    @removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.FormTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
    

 注意

Razor页面会自动受到XSRF / CSRF的保护。有关更多信息,请参阅XSRF / CSRF和Razor页面

防御CSRF攻击的最常用方法是使用同步器令牌模式(STP)。当用户请求包含表单数据的页面时,将使用STP:

  1. 服务器将与当前用户身份相关联的令牌发送到客户端。
  2. 客户端将令牌发送回服务器以进行验证。
  3. 如果服务器收到与经过身份验证的用户身份不匹配的令牌,则拒绝该请求。

令牌是唯一且不可预测的。令牌还可用于确保一系列请求的正确排序(例如,确保请求序列:第1页 - 第2页 - 第3页)。ASP.NET Core MVC和Razor Pages模板中的所有表单都会生成防伪令牌。以下一对视图示例生成防伪令牌:

CSHTML
<form asp-controller="Manage" asp-action="ChangePassword" method="post">
    ...
</form>

@using (Html.BeginForm("ChangePassword", "Manage"))
{
    ...
}

<form>不使用带有HTML帮助器的标签助手@ Html.AntiForgeryToken的情况下,明确地向元素添加防伪标记

CSHTML
<form action="/" method="post">
    @Html.AntiForgeryToken()
</form>

在上述每种情况下,ASP.NET Core都会添加一个类似于以下内容的隐藏表单字段:

CSHTML
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8NrAkS ... s2-m9Yw">

ASP.NET Core包含三个用于处理防伪令牌的过滤器

防伪选项

定制防伪选项Startup.ConfigureServices

C#
services.AddAntiforgery(options => 
{
    // Set Cookie properties using CookieBuilder properties†.
    options.FormFieldName = "AntiforgeryFieldname";
    options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
    options.SuppressXFrameOptionsHeader = false;
});

† Cookie使用CookieBuilder的属性设置防伪属性

选项描述
曲奇饼 确定用于创建防伪cookie的设置。
FormFieldName 防伪系统用于在视图中呈现防伪令牌的隐藏表单字段的名称。
HeaderName 防伪系统使用的标头名称。如果null,系统仅考虑表单数据。
SuppressXFrameOptionsHeader 指定是否禁止生成X-Frame-Options标头。默认情况下,标头生成的值为“SAMEORIGIN”。默认为false

有关更多信息,请参阅CookieAuthenticationOptions

使用IAntiforgery配置防伪功能

IAntiforgery提供API来配置防伪功能。IAntiforgery可以ConfigureStartup班级方法中申请以下示例使用应用程序主页中的中间件生成防伪令牌,并将其作为cookie发送到响应中(使用本主题后面描述的默认Angular命名约定):

C#
public void Configure(IApplicationBuilder app, IAntiforgery antiforgery)
{
    app.Use(next => context =>
    {
        string path = context.Request.Path.Value;

        if (
            string.Equals(path, "/", StringComparison.OrdinalIgnoreCase) ||
            string.Equals(path, "/index.html", StringComparison.OrdinalIgnoreCase))
        {
            // The request token can be sent as a JavaScript-readable cookie, 
            // and Angular uses it by default.
            var tokens = antiforgery.GetAndStoreTokens(context);
            context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, 
                new CookieOptions() { HttpOnly = false });
        }

        return next(context);
    });
}

需要防伪验证

ValidateAntiForgeryToken是一个动作过滤器,可应用于单个操作,控制器或全局。除非请求包含有效的防伪令牌,否则将阻止对已应用此过滤器的操作发出的请求。

C#
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RemoveLogin(RemoveLoginViewModel account)
{
    ManageMessageId? message = ManageMessageId.Error;
    var user = await GetCurrentUserAsync();

    if (user != null)
    {
        var result = 
            await _userManager.RemoveLoginAsync(
                user, account.LoginProvider, account.ProviderKey);

        if (result.Succeeded)
        {
            await _signInManager.SignInAsync(user, isPersistent: false);
            message = ManageMessageId.RemoveLoginSuccess;
        }
    }

    return RedirectToAction(nameof(ManageLogins), new { Message = message });
}

ValidateAntiForgeryToken属性需要一个令牌来请求它所装饰的动作方法,包括HTTP GET请求。如果该ValidateAntiForgeryToken属性应用于应用程序的控制器,则可以使用该IgnoreAntiforgeryToken属性覆盖该属性。

 注意

ASP.NET Core不支持自动将防伪标记添加到GET请求。

仅针对不安全的HTTP方法自动验证防伪令牌

ASP.NET Core应用程序不会为安全HTTP方法(GET,HEAD,OPTIONS和TRACE)生成防伪令牌。可以使用AutoValidateAntiforgeryToken属性而不是广泛应用ValidateAntiForgeryToken属性然后用IgnoreAntiforgeryToken属性覆盖它此属性与属性的工作方式相同,只是它不需要使用以下HTTP方法发出的请求的令牌:ValidateAntiForgeryToken

  • 得到
  • OPTIONS
  • 跟踪

我们建议AutoValidateAntiforgeryToken广泛使用非API方案。这可确保默认情况下保护POST操作。替代方法是默认忽略防伪标记,除非ValidateAntiForgeryToken应用于单个操作方法。在这种情况下,更有可能的是,POST操作方法不会被错误保护,使应用程序容易受到CSRF攻击。所有POST都应该发送防伪令牌。

API没有用于发送令牌的非cookie部分的自动机制。实现可能取决于客户端代码实现。一些例子如下所示:

类级示例:

C#
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{

全球范例:

C#
services.AddMvc(options => 
    options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute()));

覆盖全局或控制器防伪属性

所述IgnoreAntiforgeryToken滤波器用于消除对于给定动作(或控制器)的防伪标记的需要。应用时,此过滤器将覆盖更高级别(全局或控制器)上指定的过滤器ValidateAntiForgeryTokenAutoValidateAntiforgeryToken过滤器。

C#
[Authorize]
[AutoValidateAntiforgeryToken]
public class ManageController : Controller
{
    [HttpPost]
    [IgnoreAntiforgeryToken]
    public async Task<IActionResult> DoSomethingSafe(SomeViewModel model)
    {
        // no antiforgery token required
    }
}

身份验证后刷新令牌

通过将用户重定向到视图或Razor Pages页面来验证用户身份后,应刷新令牌。

JavaScript,AJAX和SPA

在传统的基于HTML的应用程序中,使用隐藏的表单字段将防伪令牌传递给服务器。在现代基于JavaScript的应用程序和SPA中,许多请求都是以编程方式进行的。这些AJAX请求可以使用其他技术(例如请求标头或cookie)来发送令牌。

如果cookie用于存储身份验证令牌并验证服务器上的API请求,则CSRF是一个潜在的问题。如果使用本地存储来存储令牌,则可能会减轻CSRF漏洞,因为本地存储中的值不会随每个请求自动发送到服务器。因此,建议使用本地存储在客户端上存储防伪令牌并将令牌作为请求头发送。

JavaScript的

将JavaScript与视图一起使用,可以使用视图中的服务创建令牌。Microsoft.AspNetCore.Antiforgery.IAntiforgery服务注入视图并调用GetAndStoreTokens

C#
@{
    ViewData["Title"] = "AJAX Demo";
}
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
    public string GetAntiXsrfRequestToken()
    {
        return Xsrf.GetAndStoreTokens(Context).RequestToken;
    }
}

<input type="hidden" id="RequestVerificationToken" 
       name="RequestVerificationToken" value="@GetAntiXsrfRequestToken()">

<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<div class="row">
    <p><input type="button" id="antiforgery" value="Antiforgery"></p>
    <script>
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
            if (xhttp.readyState == XMLHttpRequest.DONE) {
                if (xhttp.status == 200) {
                    alert(xhttp.responseText);
                } else {
                    alert(‘There was an error processing the AJAX request.‘);
                }
            }
        };

        document.addEventListener(‘DOMContentLoaded‘, function() {
            document.getElementById("antiforgery").onclick = function () {
                xhttp.open(‘POST‘, ‘@Url.Action("Antiforgery", "Home")‘, true);
                xhttp.setRequestHeader("RequestVerificationToken", 
                    document.getElementById(‘RequestVerificationToken‘).value);
                xhttp.send();
            }
        });
    </script>
</div>

这种方法消除了直接处理从服务器设置cookie或从客户端读取cookie的需要。

上面的示例使用JavaScript来读取AJAX POST标头的隐藏字段值。

JavaScript还可以访问cookie中的令牌,并使用cookie的内容创建带有令牌值的标头。

C#
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, 
    new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = false });

假设脚本请求在调用的标头中发送令牌X-CSRF-TOKEN,请配置防伪服务以查找X-CSRF-TOKEN标头:

C#
services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

以下示例使用JavaScript通过适当的标头发出AJAX请求:

JavaScript的
function getCookie(cname) {
    var name = cname + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(‘;‘);
    for(var i = 0; i <ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) == ‘ ‘) {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

var csrfToken = getCookie("CSRF-TOKEN");

var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    if (xhttp.readyState == XMLHttpRequest.DONE) {
        if (xhttp.status == 200) {
            alert(xhttp.responseText);
        } else {
            alert(‘There was an error processing the AJAX request.‘);
        }
    }
};
xhttp.open(‘POST‘, ‘/api/password/changepassword‘, true);
xhttp.setRequestHeader("Content-type", "application/json");
xhttp.setRequestHeader("X-CSRF-TOKEN", csrfToken);
xhttp.send(JSON.stringify({ "newPassword": "ReallySecurePassword999$$$" }));

AngularJS

AngularJS使用约定来解决CSRF。如果服务器发送带有名称的cookie XSRF-TOKEN,则AngularJS $http服务在向服务器发送请求时将cookie值添加到标头中。这个过程是自动的。标头不需要显式设置。标题名称是X-XSRF-TOKEN服务器应检测此标头并验证其内容。

对于ASP.NET Core API,使用此约定:

  • 配置您的应用以在名为cookie的cookie中提供令牌XSRF-TOKEN
  • 配置防伪服务以查找名为的标头X-XSRF-TOKEN
C#
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

查看或下载示例代码如何下载

扩展防伪

所述IAntiForgeryAdditionalDataProvider类型允许开发者通过往返附加数据在每个令牌延长反CSRF系统的行为。GetAdditionalData方法被调用时产生的场令牌每次,并且返回值被嵌入所生成的令牌内。实现者可以返回时间戳,随机数或任何其他值,然后在验证令牌时调用ValidateAdditionalData以验证此数据。客户端的用户名已嵌入生成的令牌中,因此无需包含此信息。如果令牌包括补充数据但未IAntiForgeryAdditionalDataProvider配置,则不验证补充数据。

防止ASP.NET Core中的跨站点请求伪造(XSRF / CSRF)攻击

标签:sub   builder   变量   error   css   cat   方案   tom   登录   

原文地址:https://www.cnblogs.com/sands/p/10289026.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!