阻止 ASP.NET 核心 cookie 身份验证接受为不同主机名签名的 cookie
Prevent ASP.NET Core cookie-authentication from accepting cookies signed for different hostnames
环境
我有一个特殊情况,在通配符域上托管一个 ASP.Core 5 Web 应用程序。
我有无数个动态子域,并且有一个单点登录 OpenID 机构负责身份验证和授权哪些用户可以访问哪些域。
例如,所有这些域都转到同一个 ASP.Core 网络应用程序,还有更多:
- device1.mydomain.io
- device2.mydomain.io
- device3.mydomain.io
- deviceN.mydomain.io
- anything.mydomain.io
如果 OIDC 重定向期间的 return URL 指向您的用户不应访问的子域,则单点登录服务器将拒绝签署您的登录名。您可以访问该特定子域,也可以不访问。
到目前为止的考虑
到目前为止,我们已将事件处理程序添加到网络服务器的 OpenID 周期中,以根据我们在重定向到单点登录服务器之前联系的 URL 动态选择 OIDC 客户端 ID。
重定向后,如果此应用程序是为与联系此应用程序的重定向 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 管理器是否是最好的方法,或者它是否解决了问题。
我找到了解决办法!
似乎 ASP.Core 默认情况下 cookie 身份验证不关心在每个请求上验证令牌时为 cookie 签名的主机名。并且可能是有充分理由的。在大多数用例中,网络服务器总是可以接受基于同一个网络服务器签署的 cookie,而不关心我们是如何联系的。
在启动期间配置 AddCookie
时,可以通过向 Events.OnValidatePrincipal
添加额外的主体验证来更改此行为。
我添加了一个额外的检查来验证为 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;
};
我觉得一旦找到这个解决方案就非常安全和直接。如果有人稍后看到并知道更好的解决方案,请告诉我。 :)
环境
我有一个特殊情况,在通配符域上托管一个 ASP.Core 5 Web 应用程序。
我有无数个动态子域,并且有一个单点登录 OpenID 机构负责身份验证和授权哪些用户可以访问哪些域。
例如,所有这些域都转到同一个 ASP.Core 网络应用程序,还有更多:
- device1.mydomain.io
- device2.mydomain.io
- device3.mydomain.io
- deviceN.mydomain.io
- anything.mydomain.io
如果 OIDC 重定向期间的 return URL 指向您的用户不应访问的子域,则单点登录服务器将拒绝签署您的登录名。您可以访问该特定子域,也可以不访问。
到目前为止的考虑
到目前为止,我们已将事件处理程序添加到网络服务器的 OpenID 周期中,以根据我们在重定向到单点登录服务器之前联系的 URL 动态选择 OIDC 客户端 ID。
重定向后,如果此应用程序是为与联系此应用程序的重定向 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 管理器是否是最好的方法,或者它是否解决了问题。
我找到了解决办法!
似乎 ASP.Core 默认情况下 cookie 身份验证不关心在每个请求上验证令牌时为 cookie 签名的主机名。并且可能是有充分理由的。在大多数用例中,网络服务器总是可以接受基于同一个网络服务器签署的 cookie,而不关心我们是如何联系的。
在启动期间配置 AddCookie
时,可以通过向 Events.OnValidatePrincipal
添加额外的主体验证来更改此行为。
我添加了一个额外的检查来验证为 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;
};
我觉得一旦找到这个解决方案就非常安全和直接。如果有人稍后看到并知道更好的解决方案,请告诉我。 :)