You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 6 MVC会话劫持防范求助(已配置基础安全仍被复现)

解决ASP.NET Core 6 MVC会话劫持问题的方案

核心问题原因

虽然配置了Secure、HttpOnly Cookie,但会话ID未与用户身份绑定。低权限用户替换Session ID后,系统仅验证会话存在,未检查会话对应的用户是否与当前请求的登录用户一致,导致权限越权。

具体解决步骤

  • 登录时将用户唯一身份标识存入Session,绑定会话与用户身份
  • 全局验证每个请求的会话用户信息与当前登录用户是否匹配,不匹配则终止请求或清除会话

示例代码

登录逻辑(AccountController)

[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        // 验证用户身份(示例逻辑)
        var user = await _userManager.FindByNameAsync(model.UserName);
        if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
        {
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.NameIdentifier, user.Id),
                new Claim(ClaimTypes.Name, user.UserName),
                new Claim(ClaimTypes.Role, (await _userManager.GetRolesAsync(user)).FirstOrDefault() ?? "User")
            };

            var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));

            // 将用户唯一ID存入Session,绑定会话与用户
            HttpContext.Session.SetString("CurrentUserId", user.Id);

            return RedirectToAction("Index", "Home");
        }
        ModelState.AddModelError("", "用户名或密码错误");
    }
    return View(model);
}

全局会话验证中间件

创建中间件类:

public class SessionAuthValidationMiddleware
{
    private readonly RequestDelegate _next;

    public SessionAuthValidationMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 跳过无需验证的路径(登录、注册等)
        var path = context.Request.Path.Value;
        if (path.StartsWith("/Account/Login") || path.StartsWith("/Account/Register") || path.StartsWith("/Home/Index"))
        {
            await _next(context);
            return;
        }

        if (context.User.Identity.IsAuthenticated)
        {
            var currentUserId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
            var sessionUserId = context.Session.GetString("CurrentUserId");

            // 验证会话用户与当前登录用户是否一致
            if (string.IsNullOrEmpty(sessionUserId) || sessionUserId != currentUserId)
            {
                // 不一致则清除登录状态与会话
                await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                context.Session.Clear();
                context.Response.Redirect("/Account/Login");
                return;
            }
        }

        await _next(context);
    }
}

注册中间件(Program.cs)

注意中间件顺序,需放在UseAuthentication和UseAuthorization之后:

var builder = WebApplication.CreateBuilder(args);

// 注册服务
builder.Services.AddControllersWithViews();
builder.Services.AddSession(options =>
{
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
    options.IdleTimeout = TimeSpan.FromMinutes(30);
});
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
        options.LoginPath = "/Account/Login";
    });

var app = builder.Build();

// 配置管道
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

// 添加自定义会话验证中间件
app.UseMiddleware<SessionAuthValidationMiddleware>();

app.UseSession();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

登出逻辑(清除Session绑定)

[HttpPost]
public async Task<IActionResult> Logout()
{
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    HttpContext.Session.Clear(); // 清除会话中的用户绑定信息
    return RedirectToAction("Login", "Account");
}

额外增强建议

  • 启用会话固定保护:登录成功后调用HttpContext.Session.Refresh()生成新Session ID,避免攻击者提前获取旧ID
  • 缩短会话超时时间:减少Session ID的有效时长,降低被滥用的窗口
  • 优先使用Claims存储身份信息:若无需Session存储其他数据,直接通过Claims传递用户身份,避免依赖Session,同时Claims随加密Cookie存储,安全性更高

内容的提问来源于stack exchange,提问作者dibas paudel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 06:35:26