防止 asp.net Web 表单中的跨站点请求伪造 (csrf) 攻击
Posted
技术标签:
【中文标题】防止 asp.net Web 表单中的跨站点请求伪造 (csrf) 攻击【英文标题】:preventing cross-site request forgery (csrf) attacks in asp.net web forms 【发布时间】:2015-07-08 11:49:54 【问题描述】:我使用 Visual Studio 2013 创建了一个 ASP.Net Web 窗体应用程序,并且我使用的是 .NET Framework 4.5。我想确保我的站点不受跨站点请求伪造 (CSRF) 的影响,我发现很多文章都在谈论如何在 MVC 应用程序上实现此功能,但很少有人谈论 Web 表单。在this *** question 上,一条评论指出
"这是一个老问题,但是最新的 Visual Studio 2012 ASP.NET Web 表单模板包括嵌入到主控中的反 CSRF 代码 页。如果您没有模板,这是代码 生成:..."
我的母版页不包含该答案中提到的代码。它真的包含在新应用程序中吗?如果没有,添加它的最佳方法是什么?
【问题讨论】:
Prevent Cross-Site Request Forgery的可能重复 @SilverlightFox,希望不是 ;) 【参考方案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 也必须添加这个标记<input name="__RequestVerificationToken" type="hidden" value="SKZi1uvLbg_G1P-KoK2AJdmeorX1fBgdCbVhLUDim9sk6AFwReVEY6XsuPrvsXJLq5MWOVaGXMnpx09srXkLM_Yjtcfg_4tpc1747jOgo941" />
。有必要吗?
这是否也验证 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 标头,则可能会破坏真实请求,这是相对常见的。以上是关于防止 asp.net Web 表单中的跨站点请求伪造 (csrf) 攻击的主要内容,如果未能解决你的问题,请参考以下文章
asp.net signalr core 中的跨域请求不起作用?
如何使用 Angular 2 在 POST 上启用 ASP.NET MVC 4 中的跨源请求
防止 ASP.NET 中的 413 错误请求实体太大 [重复]
基于.Net Framework 4.0 Web API开发:ASP.NET Web APIs AJAX 跨域请求解决办法(CORS实现)