防止 ASP.NET Core cookie 身份验证接受为不同主机名签名的 cookie

时间:2021-02-24 08:40:23

标签: asp.net asp.net-core security cookies oauth-2.0

环境
我有一个特殊情况,即在通配符域上托管单个 ASP.Core 5 Web 应用程序。

我有无限数量的动态子域,并且有一个单点登录 OpenID 机构负责身份验证和授权哪些用户可以访问哪些域。

例如,所有这些域都指向同一个 ASP.Core Web 应用程序,还有更多:

  • device1.mydomain.io
  • device2.mydomain.io
  • device3.mydomain.io
  • deviceN.mydomain.io
  • anything.mydomain.io

如果 OIDC 重定向期间的返回 URL 指向您的用户不应访问的子域,单点登录服务器将拒绝对您的登录信息进行签名。您可以访问该特定子域,也可以没有。

目前的考虑
到目前为止,我们已将事件处理程序添加到网络服务器的 OpenID 循环中,以根据我们在重定向到单点登录服务器之前联系的 URL 动态选择 OIDC 客户端 ID。

重定向后,如果该应用程序为与此应用程序联系的重定向 URL 不同的重定向 URL 签名,则该应用程序还将拒绝接受由单点登录服务器签名的令牌。这是为了防止有人复制令牌、更改 URL 并尝试将相同的令牌用于用户不应访问的不同子域。

我在 OpenID 重定向循环本身中再也看不到任何安全问题。一切正常。

问题
但是现在在使用服务时对cookie进行签名后存在安全问题。

  • 用户有权访问 domain1.mydomain.io,但无权访问 domain2.mydomain.io
  • 用户登录 domain1.mydomain.io,ASP.Core 服务签署 cookie。
  • 用户将 cookie 复制到 Postman 并使用它来联系domain2.mydomain.io
  • 现在用户也可以访问 domain2.mydomain.io,因为 ASP.Core 服务从不检查 cookie 签名的域。

如何让 ASP.Core cookie 身份验证中间件检查 cookie 签名的域,并在域与我们联系的域不同时拒绝它?

Startup.cs 代码

void AddOpenIdConnectServices(IServiceCollection services, IDataProtectionProvider dataProtectionProvider)
{
    services
        .AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie(o =>
        {
            o.DataProtectionProvider = dataProtectionProvider;
            o.Cookie.SameSite = SameSiteMode.None;
        })
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = this.config.OpenId_Authority;
            options.ClientId = this.config.OpenId_ClientId;
            options.RequireHttpsMetadata = true;
            options.SaveTokens = true;

            // Ensure that the "state" sent to the SSO server is encrypted with the same secret as the other webservers use when scaled to >1.
            // Without this login will fail because we're unable to decrypt the "state" at "signin-oidc" endpoint when coming back from the SSO-server.
            options.DataProtectionProvider = dataProtectionProvider;

            // Customize OpenID so that we can provide the SSO- server a dynamic client-id based on which hostname we were contacted on.
            // We need to intercept the redirection to the SSO- server, as well as the audience/client-id validation when the JWT- token is returned from the SSO- server.
            DynamicOpenIdClientHandler dynamicClientId = new DynamicOpenIdClientHandler(clientIdPrefix: options.ClientId);
            options.Events.OnRedirectToIdentityProvider = dynamicClientId.OnRedirectToidentityProvider;
            options.TokenValidationParameters.AudienceValidator = dynamicClientId.AudienceValidator;
            options.Events.OnTokenValidated = dynamicClientId.OnTokenValidated;
        });
}

类似问题
这是一个类似的问题,但是我不确定自定义 cookie 管理器是最好的方法,或者它是否可以解决问题。

Multiple & SubDomain's cookie in asp.net Core Identity

1 个答案:

答案 0 :(得分:0)

我找到了解决方案!

似乎默认情况下 ASP.Core cookie 身份验证不关心在对每个请求验证令牌时为 cookie 签名的主机名。并且可能是有充分理由的。在大多数用例中,网络服务器总是可以接受 cookie,只是基于同一个网络服务器签署了它,而不关心我们是如何联系的。

在启动期间配置 Events.OnValidatePrincipal 时,可以通过向 AddCookie 添加额外的主体验证来更改此行为。

我添加了一个额外的检查来验证 cookie 签名的主机名,以及当前的实际主机名。这有效,服务器不再接受为错误主机名签名的 cookie。它现在会将这些请求重定向到单点登录服务器。

o.Events.OnValidatePrincipal = context =>
{
    if (context.Properties.Items.TryGetValue("OpenIdConnect.Code.RedirectUri", out string redirectUri))
    {
        Uri cookieWasSignedForUri = new Uri(redirectUri);
        if (context.Request.Host.Host != cookieWasSignedForUri.Host)
        {
            context.RejectPrincipal();
        }
    }

    return Task.CompletedTask;
};

我觉得这个解决方案一旦找到就非常安全和直接。如果有人稍后看到并知道更好的解决方案,请告诉我。 :)