【问题标题】:preventing cross-site request forgery (csrf) attacks in asp.net web forms防止 asp.net Web 表单中的跨站点请求伪造 (csrf) 攻击
【发布时间】:2015-07-08 11:49:54
【问题描述】:

我使用 Visual Studio 2013 创建了一个 ASP.Net Web 窗体应用程序,并且我使用的是 .NET Framework 4.5。我想确保我的站点不受跨站点请求伪造 (CSRF) 的影响,我发现很多文章都在谈论如何在 MVC 应用程序上实现此功能,但很少有人谈论 Web 表单。在this StackOverflow question 上,一条评论指出

"这是一个老问题,但是最新的 Visual Studio 2012 ASP.NET Web 表单模板包括嵌入到主控中的反 CSRF 代码 页。如果您没有模板,这是代码 生成:..."

我的母版页不包含该答案中提到的代码。它真的包含在新应用程序中吗?如果没有,添加它的最佳方法是什么?

【问题讨论】:

标签: asp.net webforms csrf .net-framework-version


【解决方案1】:

您可以尝试以下方法。在 Web 表单中添加:

<%= System.Web.Helpers.AntiForgery.GetHtml() %>

这将添加一个隐藏字段和一个 cookie。因此,如果您填写一些表单数据并将其发送回服务器,您需要进行简单的检查:

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
        AntiForgery.Validate(); // throws an exception if anti XSFR check fails.
}

如果反 XSFR 检查失败,AntiForgery.Validate(); 会抛出异常。

【讨论】:

  • 命名空间是 Microsoft.AspNet.WebPages Nuget-Package 中的 System.Web.Helpers。
  • 有人知道是否有不依赖于 Razor 的可用软件包吗?
  • ashx 处理程序怎么样?这效率高吗,我在HTML 页面中有很多ajax 无法将System.Web.Helpers.AntiForgery.GetHtml() 添加到HTML 页面。
  • 我在这里看到link 也必须添加这个标记&lt;input name="__RequestVerificationToken" type="hidden" value="SKZi1uvLbg_G1P-KoK2AJdmeorX1fBgdCbVhLUDim9sk6AFwReVEY6XsuPrvsXJLq5MWOVaGXMnpx09srXkLM_Yjtcfg_4tpc1747jOgo941" /&gt;。有必要吗?
  • 这是否也验证 ajax 调用,例如使用 XMLHttpRequest 完成的调用或由 asp.net 本身完成的调用,例如 ScriptResource.axd?如果没有,那么这将不是 CSRF 攻击的可行解决方案。
【解决方案2】:

ViewStateUserKey & 双重提交 Cookie

从 Visual Studio 2012 开始,Microsoft 为新的 Web 表单应用程序项目添加了内置的 CSRF 保护。要使用此代码,请将新的 ASP .NET Web 窗体应用程序添加到您的解决方案并查看 Site.Master 代码隐藏页面。此解决方案将对所有从 Site.Master 页面继承的内容页面应用 CSRF 保护。

此解决方案必须满足以下要求:

所有进行数据修改的 Web 表单都必须使用 Site.Master 页面。 所有进行数据修改的请求都必须使用 ViewState。 该网站必须没有所有跨站点脚本 (XSS) 漏洞。有关详细信息,请参阅如何使用 Microsoft .Net Web 保护库修复跨站点脚本 (XSS)。

public partial class SiteMaster : MasterPage
{
  private const string AntiXsrfTokenKey = "__AntiXsrfToken";
  private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
  private string _antiXsrfTokenValue;

  protected void Page_Init(object sender, EventArgs e)
  {
    //First, check for the existence of the Anti-XSS cookie
    var requestCookie = Request.Cookies[AntiXsrfTokenKey];
    Guid requestCookieGuidValue;

    //If the CSRF cookie is found, parse the token from the cookie.
    //Then, set the global page variable and view state user
    //key. The global variable will be used to validate that it matches 
    //in the view state form field in the Page.PreLoad method.
    if (requestCookie != null
        && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
    {
      //Set the global token variable so the cookie value can be
      //validated against the value in the view state form field in
      //the Page.PreLoad method.
      _antiXsrfTokenValue = requestCookie.Value;

      //Set the view state user key, which will be validated by the
      //framework during each request
      Page.ViewStateUserKey = _antiXsrfTokenValue;
    }
    //If the CSRF cookie is not found, then this is a new session.
    else
    {
      //Generate a new Anti-XSRF token
      _antiXsrfTokenValue = Guid.NewGuid().ToString("N");

      //Set the view state user key, which will be validated by the
      //framework during each request
      Page.ViewStateUserKey = _antiXsrfTokenValue;

      //Create the non-persistent CSRF cookie
      var responseCookie = new HttpCookie(AntiXsrfTokenKey)
      {
        //Set the HttpOnly property to prevent the cookie from
        //being accessed by client side script
        HttpOnly = true,

        //Add the Anti-XSRF token to the cookie value
        Value = _antiXsrfTokenValue
      };

      //If we are using SSL, the cookie should be set to secure to
      //prevent it from being sent over HTTP connections
      if (FormsAuthentication.RequireSSL &&
          Request.IsSecureConnection)
      {
        responseCookie.Secure = true;
      }

      //Add the CSRF cookie to the response
      Response.Cookies.Set(responseCookie);
    }

    Page.PreLoad += master_Page_PreLoad;
  }

  protected void master_Page_PreLoad(object sender, EventArgs e)
  {
    //During the initial page load, add the Anti-XSRF token and user
    //name to the ViewState
    if (!IsPostBack)
    {
      //Set Anti-XSRF token
      ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;

      //If a user name is assigned, set the user name
      ViewState[AntiXsrfUserNameKey] =
             Context.User.Identity.Name ?? String.Empty;
    }
    //During all subsequent post backs to the page, the token value from
    //the cookie should be validated against the token in the view state
    //form field. Additionally user name should be compared to the
    //authenticated users name
    else
    {
      //Validate the Anti-XSRF token
      if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
          || (string)ViewState[AntiXsrfUserNameKey] !=
               (Context.User.Identity.Name ?? String.Empty))
      {
        throw new InvalidOperationException("Validation of " +
                            "Anti-XSRF token failed.");
      }
    }
  }
}

Source

【讨论】:

  • 我正在使用 VS2017,但在 Site.Master.cs(全新的 Web 表单应用程序项目)中没有看到此代码。
  • 我们使用这段代码大约一年,一直遇到一个问题:偶尔抛出异常。我们有我们的自定义身份验证模块来保存身份验证(长期存在的)cookie。当用户打开浏览器并导航到该站点时,它将根据该 cookie 自动登录。有一次我很幸运至少打断点并查看哪个条件失败:这是第二学期。 Context.User.Identity.Name 为空,但 ViewState 包含用户名。为什么我们还要填充和检查用户名?
  • 也许我遗漏了一些东西,但似乎攻击者可以通过查看当前页面的源代码并复制“__VIEWSTATE”和“__VIEWSTATEGENERATOR”隐藏输入,然后重新提交来轻松绕过此代码.实际上,我自己能够成功地做到这一点。您如何防止这种情况发生?
  • @ryanulit XSRF 背后的想法是有人可以伪装成来自完全不同域的客户端(例如通过
  • 有谁知道这个代码在 vb.net 中的等价物是什么?我对事件线有疑问。
【解决方案3】:

当您在 VS 2013 中创建新的“Web 表单应用程序”项目时,site.master.cs 将自动在课程的 Page_Init 部分中包含 XSRF/CSRF 代码。如果还是没有得到生成的代码,可以手动Copy+Paste代码。如果您使用的是 C#,请使用以下代码:-

private const string AntiXsrfTokenKey = "__AntiXsrfToken";
private const string AntiXsrfUserNameKey = "__AntiXsrfUserName";
private string _antiXsrfTokenValue;

 protected void Page_Init(object sender, EventArgs e)
    {
        // The code below helps to protect against XSRF attacks
        var requestCookie = Request.Cookies[AntiXsrfTokenKey];
        Guid requestCookieGuidValue;
        if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
        {
            // Use the Anti-XSRF token from the cookie
            _antiXsrfTokenValue = requestCookie.Value;
            Page.ViewStateUserKey = _antiXsrfTokenValue;
        }
        else
        {
            // Generate a new Anti-XSRF token and save to the cookie
            _antiXsrfTokenValue = Guid.NewGuid().ToString("N");
            Page.ViewStateUserKey = _antiXsrfTokenValue;

            var responseCookie = new HttpCookie(AntiXsrfTokenKey)
            {
                HttpOnly = true,
                Value = _antiXsrfTokenValue
            };
            if (FormsAuthentication.RequireSSL && Request.IsSecureConnection)
            {
                responseCookie.Secure = true;
            }
            Response.Cookies.Set(responseCookie);
        }

        Page.PreLoad += master_Page_PreLoad;
    }

    protected void master_Page_PreLoad(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Set Anti-XSRF token
            ViewState[AntiXsrfTokenKey] = Page.ViewStateUserKey;
            ViewState[AntiXsrfUserNameKey] = Context.User.Identity.Name ?? String.Empty;
        }
        else
        {
            // Validate the Anti-XSRF token
            if ((string)ViewState[AntiXsrfTokenKey] != _antiXsrfTokenValue
                || (string)ViewState[AntiXsrfUserNameKey] != (Context.User.Identity.Name ?? String.Empty))
            {
                throw new InvalidOperationException("Validation of Anti-XSRF token failed.");
            }
        }
    }

【讨论】:

  • 是的,我正在使用 C#,我尝试复制并粘贴此函数,但它似乎有多个错误。你确定这是自动生成的代码吗?
  • 无法识别 :AntiXsrfTokenKey in Request.Cookies[AntiXsrfTokenKey];并且也无法识别这些变量 _antiXsrfTokenValue, master_Page_PreLoad
  • @NadaNaeem 我已经包含了缺失的常量。
  • 谢谢,但仍然缺少master_Page_PreLoad,但没关系
  • 这里有个小bug。假设应用程序是新启动的并且没有 antixsrf cookie。用户同时发出 2 个请求(第二个请求在第一个请求结束之前开始)将为第一个请求生成一个令牌,并将其写入 antixsrf cookie。 "requestCookie != null" 将为第二个请求返回 false,因为它开始时客户端上没有 cookie。因此将生成另一个新的 antixsrf 令牌并将其写入 cookie。当第一个请求回发时,它会得到一个 xsrf 错误。因为 antixsrf cookie 值被第二个请求改变了。
【解决方案4】:

你可以使用下面的代码,它会检查请求来自哪里

if ((context.Request.UrlReferrer == null || context.Request.Url.Host != context.Request.UrlReferrer.Host)) 
    {
        context.Response.Redirect("~/error.aspx", false);
    }

它对我很有用!!!

【讨论】:

  • 这是错误和危险的。它仅将 CSRF 限制在同一主机上,并且如果剥离了 referer 标头,则可能会破坏真实请求,这是相对常见的。
猜你喜欢
  • 1970-01-01
  • 2014-06-02
  • 2017-01-09
  • 2019-05-26
  • 2015-07-14
  • 2017-01-10
  • 2016-12-26
  • 2014-08-31
  • 2011-01-11
相关资源
最近更新 更多