【问题标题】:ASP.net Identity send AntiForgeryToken from javascriptASP.net Identity 从 javascript 发送 AntiForgeryToken
【发布时间】:2024-04-12 18:30:02
【问题描述】:

ASP.net MVC 5 提供了帐户控制器和帐户视图。在注册视图中有一行

@Html.AntiForgeryToken()

上面注册控制器有属性:

[ValidateAntiForgeryToken]

我决定使用 JSON 将数据从 javascript 发送到服务器(我正在使用 Angular 和 $http.post 方法)。

我的问题是如何使用 javascript 发送这个 AntiForgeryToken 并在控制器中验证它?

还有其他我应该包括的安全措施吗?

【问题讨论】:

  • 认为您只需要查找名称为“__RequestVerificationToken”的输入并将其与您的帖子数据一起提交(使用相同的名称)。控制器应该处理所有的验证逻辑。
  • 请告诉我该怎么做? (寻找输入)
  • document.getElementsByName("__RequestVerificationToken")[0].value
  • 嗯,一些搜索让我找到了this SO post,这正是我在想的。链接to this old SO post.
  • 非常感谢,我已经做到了:)

标签: javascript asp.net asp.net-mvc angularjs asp.net-identity


【解决方案1】:

AngularJS 内置了对 XSRF(AKA 防伪)的支持

XSRF 是一种技术,未经授权的网站可以借此获取您用户的 私人数据。 Angular 提供了一种机制来对抗 XSRF。什么时候 执行 XHR 请求时,$http 服务从 cookie 中读取令牌 (默认为 XSRF-TOKEN)并将其设置为 HTTP 标头 (X-XSRF-TOKEN)。 由于只有在您的域上运行的 JavaScript 才能读取 cookie, 您的服务器可以确信 XHR 来自运行的 JavaScript 在您的域上。跨域请求不会设置header。

如果您想利用此功能,您必须创建将创建和验证 XSRF-TOKEN cookie 的操作过滤器。 这是我使用的简化版本。

AntiForgeryTokenCookieAttribute - 使用此属性添加 XSRF-TOKEN cookie

/// <summary>
/// Create a XSRF token in the XSRF-TOKEN cookie which is automatically read by AngularJS
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AntiForgeryTokenCookieAttribute : ActionFilterAttribute
{
    private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
    private const string CookieName = "XSRF-TOKEN"; //TODO: make configurable

    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        var oldVerificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);

        string newVerificationToken;
        string newToken;
        AntiForgery.GetTokens(oldVerificationToken, out newVerificationToken, out newToken);

        if (newVerificationToken != null)
        {
            _verificationTokenStore.StoreVerificationToken(filterContext.HttpContext.Response, newVerificationToken);
        }

        filterContext.HttpContext.Response.Cookies.Add(new HttpCookie(CookieName, newToken));
    }
}

ValidateAntiForgeryTokenHeaderAttribute

/// <summary>
/// Validate the XSRF token stored in the X-XSRF-TOKEN header.
/// If the header doesn't exist, look for the XSRF token in the from post.
/// 
/// Compatible with ValidateAntiForgeryTokenAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class ValidateAntiForgeryTokenHeaderAttribute : FilterAttribute, IAuthorizationFilter
{
    private readonly IAntiForgeryVerificationTokenStore _verificationTokenStore = new AntiForgeryVerificationTokenCookieStore(); //TODO: make configurable
    private const string TokenHeaderName = "X-XSRF-TOKEN"; //TODO: make configurable

    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var token = filterContext.HttpContext.Request.Headers[TokenHeaderName];
        if (token != null)
        {
            //validate the token stored in header
            var verificationToken = _verificationTokenStore.GetVerificationToken(filterContext.HttpContext.Request);
            if (verificationToken == null) { throw new HttpAntiForgeryException("Required verification token not found"); }
            AntiForgery.Validate(verificationToken, token);
        }
        else
        {
            //validate the token stored in form. Same as ValidateAntiForgeryTokenAttribute
            AntiForgery.Validate();
        }
    }
}

IAntiForgeryVerificationTokenStore

public interface IAntiForgeryVerificationTokenStore
{
    string GetVerificationToken(HttpRequestBase request);
    void StoreVerificationToken(HttpResponseBase response, string token);
}

AntiForgeryVerificationTokenCookieStore

public class AntiForgeryVerificationTokenCookieStore : IAntiForgeryVerificationTokenStore
{
    public string GetVerificationToken(HttpRequestBase request)
    {
        if (request == null) { throw new ArgumentNullException("request"); }

        var token = request.Cookies[AntiForgeryConfig.CookieName];
        return token != null ? token.Value : null;
    }

    public void StoreVerificationToken(HttpResponseBase response, string token)
    {
        if (response == null) { throw new ArgumentNullException("response"); }
        if (token == null) { throw new ArgumentNullException("token"); }

        response.Cookies.Add(new HttpCookie(AntiForgeryConfig.CookieName, token) { HttpOnly = true, Secure = AntiForgeryConfig.RequireSsl });
    }
}

【讨论】: