Cookie未设置Secure导致的问题和解决

@adens 3/16/2023 7:42:04 PM

前言

在使用ABP 7.0.2的MVC 作为UI项目时,在部署后的正式环境中所有的POST,DELETE的 AJAX请求都失败了.错误显示 [WRN] The required antiforgery header value "RequestVerificationToken" is not present.

查找问题

问题排查的过程很难受,对于开发环境中是没有一点问题的,只有部署之后会出现问题. 项目是使用Docker部署的,使用Nginx作为反向代理,并且https也是部署在Nginx里面. 一开始怀疑是部署的问题,但是经过测试.不使用Docker 和Nginx问题依旧存在.那么问题就出现在代码里面了. 根据警告首先去查了文档里关于Antiforgery的设置,一切都是正常的,之后又去ABP的Github里找Antiforgery的相关问题,一一排除也并非ABP的 Antiforgery问题. 在对比了开发环境的和正式环境的日志之后找到了问题方向 在开发环境中多了 Skipping the execution of current filter as its not the most effective filter implementing the policy Microsoft.AspNetCore.Mvc.ViewFeatures.IAntiforgeryPolicy 跳过antiforgery检查的日志.

到这里就去翻ABP的源代码了在AbpValidateAntiforgeryTokenAuthorizationFilter以及AbpAutoValidateAntiforgeryTokenAuthorizationFilter中找到了是否验证的代码

protected virtual bool ShouldValidate(AuthorizationFilterContext context)
    {
        var authCookieName = _antiForgeryCookieNameProvider.GetAuthCookieNameOrNull();

        //Always perform antiforgery validation when request contains authentication cookie
        if (authCookieName != null &&
            context.HttpContext.Request.Cookies.ContainsKey(authCookieName))
        {
            return true;
        }

        var antiForgeryCookieName = _antiForgeryCookieNameProvider.GetAntiForgeryCookieNameOrNull();

        //No need to validate if antiforgery cookie is not sent.
        //That means the request is sent from a non-browser client.
        //See https://github.com/aspnet/Antiforgery/issues/115
        if (antiForgeryCookieName != null &&
            !context.HttpContext.Request.Cookies.ContainsKey(antiForgeryCookieName))
        {
            return false;
        }

        // Anything else requires a token.
        return true;
    }


protected override bool ShouldValidate(AuthorizationFilterContext context)
    {
        if (!_options.AutoValidate)
        {
            return false;
        }

        if (context.ActionDescriptor.IsControllerAction())
        {
            var controllerType = context.ActionDescriptor
                .AsControllerActionDescriptor()
                .ControllerTypeInfo
                .AsType();

            if (!_options.AutoValidateFilter(controllerType))
            {
                return false;
            }
        }

        if (IsIgnoredHttpMethod(context))
        {
            return false;
        }

        return base.ShouldValidate(context);
    }

这里写了不验证AntiForgery的情况. 在代码里的配置也是没有问题.推断出问题应该在Cookie传参的问题了,打开浏览器发现没有XSRF-TOKEN这个Cookie. 但是在abp的文档里.XSEF-TOKEN是自动设置的.abp文档 Github也没有相关issue.无奈只能去看源码 在源码里找设置Cookie的地方 在AspNetCoreAbpAntiForgeryManager里写了方法

    public virtual void SetCookie()
    {
        HttpContext.Response.Cookies.Append(
            Options.TokenCookie.Name,
            GenerateToken(),
            Options.TokenCookie.Build(HttpContext)
        );
    }

再找到引用该方法的地方.发现有四个,随便打开一个 AbpApplicationConfigurationController

[HttpGet]
    public virtual async Task<ApplicationConfigurationDto> GetAsync(
        ApplicationConfigurationRequestOptions options)
    {
        _antiForgeryManager.SetCookie();
        return await _applicationConfigurationAppService.GetAsync(options);
    }

这个方法是所有页面都会使用到的,也就是自动设置了Cookie. 在日志的开头往前的部分看到Cookie设置错误的警告 [WRN] The cookie 'XSRF-TOKEN' has set 'SameSite=None' and must also set 'Secure'. 两个警告差的相当远,现在看应该是Secure的问题了

开测试项目验证一下

验证结果

设置了SameSite=None 但是没有设置 Secure=true在我使用的版本.NET 7 中会阻止生成Cookie

代码验证

  HttpContext.Response.Cookies.Append("test", "123", new CookieOptions
        {
            SameSite = SameSiteMode.None,
            Secure = false
        });

在ASP.NET 中的警告内容

Pasted image 20230317095535.png

如果设置了secure=true就正常设置了

解决方案

  1. 改代码加上Secure=ture 但是这一段代码是ABP中的源码.不好轻易改, 就有了下面的解决办法

  2. 添加CookiePolicy

 app.UseCookiePolicy(new CookiePolicyOptions
        {
            Secure = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always
        }) ;

这样所有的Cookie都会被设置成Secure=true

Last Modification : 3/16/2023 9:10:24 PM


In This Document