我正在向混合WebForms / MVC站点添加一些UI功能。在这种情况下,我将一些AJAX UI功能添加到WebForms页面(通过jQuery),数据来自MVC JsonResult。一切都在100%工作,但有一个例外:
我想实现AntiForgeryToken的XSRF保护。我已经将它与我的纯MVC应用程序上的ValidateAntiForgeryToken属性结合使用,但是想知道如何在WebForms中实现Html.AntiForgeryToken()方法。 Here's an example using a UrlHelper
我在查看ViewContext / RequestContext“正确”时遇到了一些麻烦。我应该如何在WebForms页面中使用HtmlHelpers?
修改: 我想从我的WebForms页面检索AntiForgeryToken,而不是从MVC JsonResult检索。
答案 0 :(得分:18)
我知道这是一个老问题,但我今天遇到了这个问题并且认为我会分享。我在MVC4中工作并拥有一个在MVC(通过RenderPartial)和WebForms之间共享的webform控件(.ascx)。在那个控件中,我需要一个防伪令牌。幸运的是,现在你可以在你的网络表格中使用帮助,这很简单:
<%= AntiForgery.GetHtml() %>
这会像你在MVC中那样呈现你的防伪标记。
答案 1 :(得分:6)
关键方法在MVC源代码中:GetAntiForgeryTokenAndSetCookie
这将创建一个名为AntiForgeryData
的内部密封类的实例。
该实例被序列化为cookie“__RequestVerificationToken_”+应用程序路径的base 64编码版本。
同一个AntiForgeryData
实例被序列化为隐藏输入。
AntiForgeryData
的独特部分带有RNGCryptoServiceProvider.GetBytes()
所有这些都可能在WebForms页面中被欺骗,唯一的混乱是隐藏密封类的序列化。不幸的是,关键方法(GetAntiForgeryTokenAndSetCookie
)依靠ViewContext.HttpContext.Request
来获取Cookie,而WebForm则需要使用HttpContext.Current.Request
。
<强>更新强>
没有太多的测试和很多代码,但我想我已经通过一点反思解决了这个问题。在我使用反射的地方,我留下了上面注释的等效线:
using System;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
/// <summary>Utility to provide MVC anti forgery tokens in WebForms pages</summary>
public class WebFormAntiForgery
{
/// <summary>Create an anti forgery token in a WebForms page</summary>
/// <returns>The HTML input and sets the cookie</returns>
public static string AntiForgeryToken()
{
string formValue = GetAntiForgeryTokenAndSetCookie();
// string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
var mvcAssembly = typeof(HtmlHelper).Assembly;
var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData");
string fieldName = Convert.ToString(afdType.InvokeMember(
"GetAntiForgeryTokenName",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[] { null }));
TagBuilder builder = new TagBuilder("input");
builder.Attributes["type"] = "hidden";
builder.Attributes["name"] = fieldName;
builder.Attributes["value"] = formValue;
return builder.ToString(TagRenderMode.SelfClosing);
}
static string GetAntiForgeryTokenAndSetCookie()
{
var mvcAssembly = typeof(HtmlHelper).Assembly;
var afdType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryData");
// new AntiForgeryDataSerializer();
var serializerType = mvcAssembly.GetType("System.Web.Mvc.AntiForgeryDataSerializer");
var serializerCtor = serializerType.GetConstructor(new Type[0]);
object serializer = serializerCtor.Invoke(new object[0]);
// string cookieName = AntiForgeryData.GetAntiForgeryTokenName(HttpContext.Current.Request.ApplicationPath);
string cookieName = Convert.ToString(afdType.InvokeMember(
"GetAntiForgeryTokenName",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[] { HttpContext.Current.Request.ApplicationPath }));
// AntiForgeryData cookieToken;
object cookieToken;
HttpCookie cookie = HttpContext.Current.Request.Cookies[cookieName];
if (cookie != null)
{
// cookieToken = Serializer.Deserialize(cookie.Value);
cookieToken = serializerType.InvokeMember("Deserialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookie.Value });
}
else
{
// cookieToken = AntiForgeryData.NewToken();
cookieToken = afdType.InvokeMember(
"NewToken",
BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,
null,
null,
new object[0]);
// string cookieValue = Serializer.Serialize(cookieToken);
string cookieValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { cookieToken }));
var newCookie = new HttpCookie(cookieName, cookieValue) { HttpOnly = true };
HttpContext.Current.Response.Cookies.Set(newCookie);
}
// AntiForgeryData formToken = new AntiForgeryData(cookieToken)
// {
// CreationDate = DateTime.Now,
// Salt = salt
// };
var ctor = afdType.GetConstructor(new Type[] { afdType });
object formToken = ctor.Invoke(new object[] { cookieToken });
afdType.InvokeMember("CreationDate", BindingFlags.SetProperty, null, formToken, new object[] { DateTime.Now });
afdType.InvokeMember("Salt", BindingFlags.SetProperty, null, formToken, new object[] { null });
// string formValue = Serializer.Serialize(formToken);
string formValue = Convert.ToString(serializerType.InvokeMember("Serialize", BindingFlags.InvokeMethod, null, serializer, new object[] { formToken }));
return formValue;
}
}
用法类似于MVC:
WebFormAntiForgery.AntiForgeryToken()
它创建与MVC方法相同的cookie和相同的HTML。
我没有对salt和domain方法感到困扰,但它们很容易添加。
答案 2 :(得分:1)
默认情况下,ASP.NET WebForms已包含用于验证事件和视图状态的度量。 Phil Haack在链接的帖子中谈到了一点。讨论了XSRF缓解策略here (Scott Hanselman)和here (Dino Esposito)
答案 3 :(得分:0)
您可以在控制器中创建一个新的HtmlHelper,然后从那里拉出反xrsf:
var htmlHelper = new HtmlHelper(
new ViewContext(
ControllerContext,
new WebFormView("omg"),
new ViewDataDictionary(),
new TempDataDictionary()),
new ViewPage());
var xsrf = htmlHeler.AntiForgeryToken;
myObject.XsrfToken = xsrf;
return JsonResult(myObject);