如何解决Classic ASP.Net MVC网站的OAuth2重放攻击问题?
一、服务器端会话跟踪验证
Classic ASP.NET没有现成的ITicketStore,但可以自行实现类似的服务器端会话校验逻辑,核心是让每个认证Cookie绑定一个服务器端记录的会话状态,注销时标记会话无效:
- 登录时绑定会话ID
登录成功后,生成唯一会话ID,存入认证Claims和服务器端缓存(或数据库、Redis):
var identity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie); var sessionId = Guid.NewGuid().ToString(); // 将会话ID加入Claims identity.AddClaim(new Claim("ServerSessionId", sessionId)); // 服务器端缓存会话,有效期与Cookie一致 HttpRuntime.Cache.Insert( sessionId, "Active", null, DateTime.Now.AddMinutes(30), Cache.NoSlidingExpiration ); // 完成登录 HttpContext.GetOwinContext().Authentication.SignIn( new AuthenticationProperties { IsPersistent = false }, identity );
- 注销时销毁服务器端会话
注销操作中,不仅清除客户端Cookie,还要删除服务器端的会话记录:
public ActionResult Logout() { var authManager = HttpContext.GetOwinContext().Authentication; var claimsIdentity = User.Identity as ClaimsIdentity; var sessionId = claimsIdentity?.FindFirst("ServerSessionId")?.Value; if (!string.IsNullOrEmpty(sessionId)) { HttpRuntime.Cache.Remove(sessionId); } // 清除客户端Cookie authManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); return RedirectToAction("Login", "Account"); }
- 全局请求校验会话有效性
在Global.asax.cs的Application_AuthenticateRequest事件中,对每个已认证请求校验服务器端会话状态:
protected void Application_AuthenticateRequest(object sender, EventArgs e) { if (User?.Identity?.IsAuthenticated == true) { var claimsIdentity = User.Identity as ClaimsIdentity; var sessionId = claimsIdentity?.FindFirst("ServerSessionId")?.Value; // 会话ID不存在或服务器端无记录,强制注销 if (string.IsNullOrEmpty(sessionId) || HttpRuntime.Cache.Get(sessionId) == null) { var authManager = HttpContext.Current.GetOwinContext().Authentication; authManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); Response.Redirect("/Account/Login"); Response.End(); } } }
二、同步Okta端会话注销
利用Okta的OAuth2注销端点,在应用注销时同时终止Okta端的用户会话,这样即使客户端留存旧Cookie,应用向Okta验证令牌时会被拒绝:
public ActionResult Logout() { var authManager = HttpContext.GetOwinContext().Authentication; authManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie); // 构造Okta注销URL,需提前在Okta控制台配置允许的post_logout_redirect_uri var oktaDomain = "https://your-okta-domain.com"; var clientId = "your-okta-client-id"; var redirectUri = Url.Action("Login", "Account", null, Request.Url.Scheme); var logoutUrl = $"{oktaDomain}/oauth2/v1/logout?client_id={clientId}&post_logout_redirect_uri={Uri.EscapeDataString(redirectUri)}"; return Redirect(logoutUrl); }
三、增强Cookie安全属性
通过配置Cookie的安全属性,降低Cookie被窃取和重放的风险(需.NET Framework 4.7.2及以上支持SameSite):
在Startup.Auth.cs的Cookie认证配置中添加:
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), // 禁止前端脚本访问Cookie CookieHttpOnly = true, // 仅HTTPS环境下发送Cookie CookieSecure = CookieSecureOption.Always, // 限制Cookie跨站发送 CookieSameSite = SameSiteMode.Strict, // 缩短Cookie有效期,减少重放窗口 ExpireTimeSpan = TimeSpan.FromMinutes(15), // 滑动刷新有效期 SlidingExpiration = true });
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

