使用identityserver4和aspnet身份时如何安全地实现空闲超时?

时间:2019-07-12 15:41:10

标签: javascript jquery asp.net-identity asp.net-core-2.0 identityserver4

我正在使用.NET Core 2.2 Razor页面中的.NET Identity在Identityserver4登录站点(服务器UI)上工作,我有一个javascript模态警报,警告用户未决的空闲超时,然后在达到超时时重定向通过设置window.location

进入注销屏幕

我遇到的麻烦是,快速入门示例中的OnGet显示了一个用户退出提示,因为此时logoutId为null。我想注销而不提示用户。

目前,我通过向“注销”页面传递“ autoLogout”参数来解决此问题,该参数绕过了logoutId的检查并设置了ShowLogoutPrompt = false。我知道这有点违背了检查logoutId以确保在没有提示的情况下安全注销的目的。

是否有更好的方法来做我想做的事情?

编辑2019年7月16日: 似乎处理空闲超时的“正确”方法是设置应用程序cookie的令牌到期(例如20分钟)并启用SlidingExpiration,以便在用户刷新时更新令牌。有关此的详细信息,请参阅MS文档中的this blog postthis github issue thread (including comments from Brock Allen)this info

我的麻烦是此解决方案有两个巨大的缺点。

  1. SlidingExpiration仅在令牌的TimeSpan超过50%的用户刷新cookie(请参阅MS文档here中的SlidingExpiration信息)。因此,如果将9m59s刷新为20分钟的令牌,则它们将在10分钟而不是20分钟后超时。一种解决方法是将令牌寿命设置为40分钟,这将使用户至少有20分钟的空闲时间,但是他们可以最多有40分钟的空闲时间,这是不可接受的。
  2. 我的要求之一是一种模式,用于警告用户即将超时,并为他们提供继续/注销的选项。为此,我需要使用Javascript(或至少在C#的Razor Page中)的cookie从cookie中读取令牌的到期时间,以便我有时间显示警告。即使没有模式要求,我也需要知道令牌何时过期,以便可以刷新页面以将用户发送到登录屏幕。我正在尝试使用以下代码读取到期时间,但是在刷新令牌后直到第二次刷新页面之前,它无法读取正确的到期时间,我不知道为什么。
    @(DateTime.Parse(((await Context.AuthenticateAsync()).Properties.Items)[".expires"]))

Cookie方法的另一个不太重要的缺点是,如果我设法实现模式弹出窗口,并且用户选择继续,则页面将需要刷新以获取新令牌,这时所有未保存的数据都将丢失。我想如果它们超时了,尽管如此,未保存的数据仍然会丢失,因此与上述相比,这是一个相对较小的问题。

我正在考虑回到具有所需功能的原始解决方案,但攻击者可能会滥用它,因为攻击者在空闲超时javascript中注意到了我的autoLogout参数,然后可以使用它来提供注销的热链接页。目前,冒险是我最好的选择。

我觉得我在这上面掉了一个兔子洞,但仍然没有好的解决方案。令我惊讶的是,我想这是一种常见的用例(空闲超时,并带有警告消息,允许用户继续/注销),但是使用这种身份验证技术的情况却是如此。我想念什么吗?我的杆子末端错了吗?

2 个答案:

答案 0 :(得分:0)

经过深思熟虑,我得出的结论是前线无法启动注销。它违反了永远不信任客户的原则。换句话说,可以说,如果用户设法禁用javascript,则它可以永远保持登录状态。

因此,您应该从IdentityServer内部触发注销。为此,您可以创建一个本地服务,其行为类似于已经存在的TokenCleanup service

该服务可以跟踪用户并触发注销。某种“内部反向通道”注销。具有相同的经验:用户尝试与服务器联系后,就会发现自己已注销,因为前端不知道注销。

您可以通过以下两种方法解决此问题:添加代码以向前端发出信号以刷新页面或保留您已有的代码,但仅在会话到期后刷新页面。

答案 1 :(得分:0)

我在这里发布我的最终解决方案。它有效,但我不太喜欢。作为参考,详细说明了为什么我认为它有点笨拙,以及我认为主要缺点是在7月16日对上述原始问题的编辑。

在添加Identityserver之后,在ConfigureServices中,我设置了Cookie的SlidingExpiration = true; ExpireTimeSpan = AppSettings.IdleTimeoutMins(有关如何设置AppSettings,请参见this post

// Rename the .AspNetCore.Identity.Application cookie and set up for idle timeout
services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Name = "xxxx.Application";
    options.SlidingExpiration = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(_config.GetValue<int>("AppSettings:" + nameof(AppSettings.IdleTimeoutMins)));
});

我有一个“局部剃刀”页面,在其中有javascript代码,可以使用倒数计时器向用户显示模式警报。我从AppSettings.IdleTimeoutMins获得timeoutSeconds,并且还有一个设置来确定何时显示警告。有关此位(及其优点和缺点)的更多详细信息,请参见此处的其他问题和解答:this blog警告消息为用户提供了“继续”选项,该选项可刷新页面(并因此刷新身份验证凭单)或“注销”,将其发送到“注销确认”页面。如果时钟用尽,则会刷新页面,这将使它们返回“登录”屏幕。

在Partial顶部:

@inject RussellLogin.Services.IAppSettings AppSettings;
@using Microsoft.AspNetCore.Authentication;

获取故障单上剩余的(假定)秒数:

secondsRemaining = "@(DateTime.Parse(((await AuthenticationHttpContextExtensions
                                             .AuthenticateAsync(Context))
                                             .Properties
                                             .Items)[".expires"])
                     .Subtract(DateTime.Now)
                     .TotalSeconds)";
// If secondsRemaining is less than half the expiry timespan then assume it will be re-issued
if (secondsRemaining < timeoutSeconds / 2) {
    secondsRemaining = timeoutSeconds;
}