八、避免 Web API 中的跨站点请求伪造攻击

本章将帮助您在 ASP 中避免跨站点请求伪造(CSRF)。 净 Web API。 使用基于 API 密钥的认证或更复杂的机制(如 OAuth)来避免 CSRF 攻击。

在本章中,我们将涵盖以下主题:

  • 什么是 CSRF 攻击?
  • 使用 HTML 表单或 Razor 视图的防伪令牌
  • 使用 AJAX 防伪令牌

什么是 CSRF 攻击?

在维基百科(https://en.wikipedia.org/wiki/Cross-site_request_forgery/),跨站点请求伪造(CSRF 或 XSRF),也称为一键攻击或会话骑,是一种恶意的利用网站,未经授权的命令是传播从一个网站的用户信任。 与跨站点脚本编制(XSS)不同,它利用用户对特定站点的信任,而 CSRF 利用站点在用户浏览器中的信任。

简单地说,这种类型的攻击是由恶意站点向登录到易受攻击站点的用户发送请求时发起的。

What is a CSRF attack?

图 1 - CSRF 攻击如图所示

使用 HTML 表单或 Razor 视图的防伪令牌

防伪令牌或请求验证令牌在 ASP 中使用.NET MVC 避免 CSRF 攻击。 防伪令牌或请求验证令牌有助于防止 CSRF 攻击。 . net 框架内置了创建和验证防伪令牌的支持。 MVC Razor 引擎中的@Html.AntiForgeryToken()方法创建了防伪令牌。 防伪令牌的验证可以通过使用[ValidateAntiForgeryToken]属性修饰控制器或动作来实现。

防伪代币是如何工作的?

让我们看看服务器如何基于防伪令牌接受或拒绝请求。 以下是制作防伪代币所涉及的步骤:

  1. 客户端发送一个带有表单的 MVC 视图请求。
  2. 服务器返回请求的视图和两个标记,一个通过 cookie 发送,另一个通过设置为视图形式的隐藏字段发送。
  3. 当客户端在view中提交表单时,这两个令牌将被传递回服务器。
  4. 服务器在连续的请求中查找这两个令牌,如果没有找到令牌,那么请求将被拒绝。

防伪令牌的保护和认证令牌的保护一样重要。 因此,使用 SSL 是最佳实践。 参考第二章为 ASP 启用 SSL.NET Web API来查看如何为 ASP 启用SSL。 净 Web API。

使用 AJAX 防伪令牌

使用 AJAX 向服务器发送数据或从服务器获取数据是非常常见的。 AJAX 请求将 JSON 数据发送到服务器。 它不发送 HTML 表单数据。 为了通过 AJAX 发送令牌,我们需要使用自定义 HTTP 头。 使用 Razor 语法,我们可以通过调用AntiForgery.GetTokens()方法来生成令牌,并将其附加到请求中,如下代码所示:

<script>
    @functions{
        public string GetAntiForgeryTokenValue ()
        {
            string tokenInCookie, tokenInForm;
            AntiForgery.GetTokens(null, out tokenInCookie, out tokenInForm);
            return tokenInCookie + ":" + tokenInForm;                
        }
    }

    $.ajax("/api/contacts", {
            type: "get",
            headers: {
                'AntiForgeryToken': '@GetAntiForgeryTokenValue()'
                },
                success: function (result) {
                    alert(JSON.stringify(result));
                },
                error: function (XMLHttpRequest, textStatus, errorThrown) {
                    alert(errorThrown + " Error");
                }
            });
</script>

当处理请求时,应该提取通过 AJAX 传递的令牌。 然后,必须使用以下代码片段中给出的AntiForgery.Validate方法验证提取的令牌。 如果令牌在请求中无效,AntiForgery.Validate方法抛出异常,如下所示:

void ValidateToken(HttpRequestMessage request)
{
    string tokenInCookie = "";
    string tokenInForm = "";

    IEnumerable<string> tokenHeaders;
    if (request.Headers.TryGetValues("AntiForgeryToken", out tokenHeaders))
    {
        var tokens = tokenHeaders.First().Split(':');
        if (tokens.Length == 2)
        {
            tokenInCookie = tokens[0].Trim();
            tokenInForm = tokens[1].Trim();
        }
    }
    AntiForgery.Validate(tokenInCookie, tokenInForm);
}

我们可以为 Web API 实现一个授权过滤器,并将其修饰为动作,以便在调用动作之前验证防伪造令牌。 下面的代码片段展示了这样一个授权过滤器:

namespace Chapter08.AntiForgeryToken.Filters
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
    public class ValidateAntiForgeryTokenAttribute : FilterAttribute, IAuthorizationFilter
    {
        public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
        {
            try
            {
                string tokenInCookie = "";
                string tokenInForm = "";

                IEnumerable<string> AntiForgeryTokenValue;
                if (actionContext.Request.Headers.TryGetValues("AntiForgeryToken", out AntiForgeryTokenValue))
                {
                    var antiForgeryTokens = AntiForgeryTokenValue.First().Split(':');
                    if (antiForgeryTokens.Length == 2)
                    {
                        tokenInCookie = antiForgeryTokens [0].Trim();
                        tokenInForm = antiForgeryTokens [1].Trim();
                    }
                }
                AntiForgery.Validate(tokenInCookie, tokenInForm);
            }
            catch (System.Web.Mvc.HttpAntiForgeryException e)
            {
                actionContext.Response = new HttpResponseMessage
                {
                    StatusCode = HttpStatusCode.Forbidden,
                    RequestMessage = actionContext.ControllerContext.Request
                };
                var response = new TaskCompletionSource<HttpResponseMessage>();
                response.SetResult(actionContext.Response);
                return response.Task;
            }
            return continuation();
        }
    }
}

CSRF 攻击将被服务器以403 Forbidden错误拒绝。

总结

又短又甜,不是吗? 您刚刚学习了如何保护我们的 Web API 免受跨站点请求伪造攻击。

您还了解了 CSRF 攻击的含义以及它如何影响我们的 Web API。

然后学习了如何使用 HTML 表单和 AJAX 实现防伪令牌。

在下一章中,让我们看看如何在 Web API 中启用跨源资源共享。

让我们来看看起源吧!