为什么 Session.Clear() - 不起作用?

Why Session.Clear() - doesn't work?

我正在使用 .NET Core 2.1,并尝试使用 Session 作为身份验证的一部分。我期待发生的是:

  1. 人登录
  2. 我设置会话值
  3. 返回会话 cookie
  4. 人点击注销
  5. 我通过HttpContext.Session.Clear()
  6. 清除会话

然后我试图伪造一个Session cookie被从原来的人那里偷走的情况,所以我向服务器创建另一个请求,并且期望session中设置的值不会存在。看起来即使在调用 Session.Clear().

之后它仍然持有它

代码:

Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {     
          services.AddSession(options =>
          {
                options.IdleTimeout = TimeSpan.FromMinutes(10);
                options.Cookie.Name = SessionCookieName;
                options.Cookie.HttpOnly = true;
          });
    }



    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.UseSession();
    }

登录操作:

HttpContext.Session.SetInt32(SessionConstants.Key, value);

注销操作:

HttpContext.Session.Clear();

检查登录状态操作:

 var sessionValue = HttpContext.Session.GetInt32(SessionConstants.Key);

测试题:

    var httpClientGenuineRequest = GetClient();
                var httpClientAttacker = GetClient();

                //Given
                var request = new LoginRequest()
                {
                   //filled properties
                };

                var loginResponse = await httpClientGenuineMember.PostAsync("urlToLoginAction", new JsonContent(request));
                AddResponseCookiesToHeaders(httpClientAttacker, loginResponse);

                await httpClientGenuineMember.PostAsync("urlToLogoutAction", new JsonContent(request));

                //When
                var response = await httpClientAttacker.GetAsync("urlToCheckStatusAction");

                //Here the actual result is true
                var actualResult = response.GetResult<bool>();

                //Then
                var expectedResult = false;

上面的例子不完整。问题仅与会话值有关,因此我删除了所有其他身份验证代码以使其更加清晰。

在我看来,HttpContext.Session.Clear 没有清除会话,或者值存储在会话 Cookie 中(这没有任何意义)?

此外,当我调试并检查我的密钥的会话值时,紧接着 Clear(),它给我值 null。但是当我传递会话 cookie 并尝试读取该值时,它又出现了。

显然,当 Idle Timeout 用完时,代码的行为符合预期。不过,在那段时间里这听起来有点安全风险。

如有任何帮助,我们将不胜感激。

编辑:

根据post下面的评论,我尝试将Redis会话添加到其中:

services.AddDistributedRedisCache(setup =>
            {
                setup.Configuration = "urlToRedisInstance";
                setup.InstanceName = "MySessionStateStore2";
            });

但行为完全相同。

原因是(听起来很荒谬)是因为您没有清除正确的会话变量。

根据 Microsoft docs .net core 2.1 中的会话在以下两种情况之一发生时结束:

Session data is deleted either when the ISession.Clear implementation is called or when the session expires.

然后是以下状态:

There's no default mechanism to inform app code that a client browser has been closed or when the session cookie is deleted or expired on the client.

这告诉我们两件事:

1)如果你想使用最大过期的会话,你将不得不自己这个机制来当客户端会话过期或浏览器关闭时通知您的服务器。否则,即使浏览器关闭,您也可能会保留会话数据(这是一种荒谬的情况,但确实存在)。

2) ISession.Clear 方法Microsoft.AspNetCore.Http 名称 space 的一部分,意思是当您调用 HttpContext.Session.Clear(); 时,您基本上什么都没有清除。

正如我所说,这种情况可能很痛苦,但却是一个活生生的事实,如果我们选择使用 .net core 2.1 和会话状态,我们的代码必须知道如何处理。

挖掘代码后我发现了问题:

问题出在我的测试中。我将登录请求响应 cookie 设置为 "attacker's" httpClient 但不是实际用户。结果,它没有得到 Session Cookie,所以我的 Session.Clear() 实际上清除了新创建的会话,而不是在登录过程中创建的会话。

更正测试:

var httpClientGenuineRequest = GetClient();
                var httpClientAttacker = GetClient();

                //Given
                var request = new LoginRequest()
                {
                   //filled properties
                };

                var loginResponse = await httpClientGenuineMember.PostAsync("urlToLoginAction", new JsonContent(request));
                AddResponseCookiesToHeaders(httpClientGenuineMember, loginResponse); // THAT LINE MADE THE DIFFERENCE
                AddResponseCookiesToHeaders(httpClientAttacker, loginResponse);

                await httpClientGenuineMember.PostAsync("urlToLogoutAction", new JsonContent(request));

                //When
                var response = await httpClientAttacker.GetAsync("urlToCheckStatusAction");

                //Here the actual result is true
                var actualResult = response.GetResult<bool>();

                //Then
                var expectedResult = false;

Session.Clear() 完全符合预期。