Тайм‑аут сессии в .NET 4.7.2 с OWIN Identity от Microsoft
Полное руководство по тайм‑ауту сессии в .NET 4.7.2 с OWIN Identity: настройка, перенаправление и кэш‑контроль согласно OWASP для безопасной работы для защиты.
Как реализовать таймаут сессии в приложении .NET Framework 4.7.2, использующем .NET Identity и OWIN?
У нас есть несколько приложений, построенных на .NET Framework 4.7.2, которые используют .NET Identity для авторизации и аутентификации. Мы пытаемся реализовать таймаут сессии в соответствии с рекомендациями OWASP.
Мы попробовали добавить конфигурацию sessionState в файл web.config:
<configuration>
<system.web>
<sessionState timeout="2" />
</system.web>
</configuration>
Однако это, по‑видимому, не оказывает никакого эффекта (значение 2 минуты используется только для тестирования).
Мы подозреваем, что существует специфический способ реализации таймаута сессии с помощью .NET Identity/OWIN, например, вызов функции invalidation сессии, но не можем найти подробной документации по этому поводу.
Также неясно, какое поведение ожидается от таймаута:
- Должна ли страница автоматически перенаправлять на страницу входа?
- Могут ли возникнуть проблемы, если приложение не отключает кэширование страниц?
Могут ли кто‑то предоставить подробные рекомендации по правильной реализации таймаута сессии в приложении .NET Framework 4.7.2 с использованием .NET Identity?
Для реализации таймаута сессии в приложении .NET Framework 4.7.2, использующем .NET Identity и OWIN, необходимо настроить свойство ExpireTimeSpan в CookieAuthenticationOptions в файле Startup.cs. Конфигурация sessionState в web.config не влияет на сессии аутентификации OWIN. После истечения таймаута пользователь будет автоматически перенаправлен на страницу входа, но также следует добавить корректные заголовки управления кэшем, чтобы предотвратить отдачу закешированных страниц после истечения сессии.
Содержание
- Понимание таймаута сессии в OWIN Identity
- Настройка ExpireTimeSpan в Startup.cs
- SlidingExpiration против фиксированного истечения
- Поведение перенаправления и проблемы кэширования
- Полный пример реализации
- Устранение распространенных проблем
Понимание таймаута сессии в OWIN Identity
При использовании .NET Identity с аутентификацией OWIN таймаут сессии управляется иначе, чем в традиционном состоянии сессии ASP.NET. Конфигурация sessionState в web.config в основном влияет на состояние сессии ASP.NET, которое отделено от управления сессией аутентификации OWIN.
Согласно исследованиям из обсуждений Stack Overflow, разработчики часто ошибочно полагают, что настройки sessionState в web.config контролируют таймауты аутентификации OWIN. Это не так, потому что OWIN использует собственный механизм cookie‑аутентификации.
Сессия аутентификации в OWIN Identity управляется через истечение cookie, которое контролируется свойством CookieAuthenticationOptions в файле Startup.cs. Таймаут sessionState может быть актуален для других функций, зависящих от сессии, но не влияет на автоматический выход из системы Identity.
Настройка ExpireTimeSpan в Startup.cs
Основной способ установки таймаута сессии в OWIN Identity – через свойство ExpireTimeSpan в CookieAuthenticationOptions. Это свойство определяет, как долго cookie аутентификации остаётся действительным.
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(30), // Установите желаемый таймаут
SlidingExpiration = true
});
}
Как отмечено в нескольких ответах Stack Overflow, необходимо задать ExpireTimeSpan в нужную длительность. Значение должно быть объектом TimeSpan, представляющим время, в течение которого cookie аутентификации будет действительным.
Например:
- 30 минут:
TimeSpan.FromMinutes(30) - 1 час:
TimeSpan.FromHours(1) - 4 часа:
TimeSpan.FromHours(4)
Вы также можете сделать это конфигурируемым через настройки web.config:
public void ConfigureAuth(IAppBuilder app)
{
var timeoutInMinutes = int.Parse(ConfigurationManager.AppSettings["SessionTimeout"]);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(timeoutInMinutes),
SlidingExpiration = true
});
}
SlidingExpiration против фиксированного истечения
Настройка SlidingExpiration в CookieAuthenticationOptions существенно влияет на поведение таймаута сессии:
SlidingExpiration = true (по умолчанию):
- Время истечения cookie продлевается при каждом запросе
- Пользователь остаётся залогиненным, пока продолжает делать запросы
- Фактический таймаут наступает только после того, как пользователь перестанет делать запросы в течение заданного периода
- Предоставляет лучший пользовательский опыт, но может не соответствовать строгим требованиям безопасности
SlidingExpiration = false:
- Cookie истекает в фиксированное время от момента входа
- Пользователь выходит из системы ровно через заданное время после входа
- Более безопасно для соблюдения требований, например, PCI DSS
- Пользователи с постоянной активностью всё равно будут выведены из системы в фиксированное время
Согласно руководству по безопасности Optimizely, для соответствия PCI обычно следует устанавливать SlidingExpiration в false и использовать более короткий таймаут, например, 15 минут.
Поведение перенаправления и проблемы кэширования
Автоматическое перенаправление
Когда cookie аутентификации OWIN истекает, ASP.NET Identity автоматически перенаправляет пользователей на страницу входа, указанную в настройке LoginPath. Это происходит автоматически при доступе к защищённому действию или контроллеру после истечения таймаута.
Однако вы можете настроить это поведение, изменив обработчик OnApplyRedirect:
public void ConfigureAuth(IAppBuilder app)
{
CookieAuthenticationProvider provider = new CookieAuthenticationProvider();
Action<CookieApplyRedirectContext> originalHandler = provider.OnApplyRedirect;
provider.OnApplyRedirect = context =>
{
context.RedirectUri = $"{new PathString("/Account/Login")}/?session_expired=1";
originalHandler.Invoke(context);
};
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// ... другие параметры
Provider = provider
});
}
Это позволяет добавить параметр запроса при перенаправлении из‑за истечения сессии, который можно использовать для отображения конкретного сообщения пользователям.
Проблемы кэширования
Одна из значительных проблем с таймаутом сессии – браузеры могут кэшировать страницы, отдавая их даже после того, как сессия пользователя истекла. Это может создать уязвимость безопасности, когда пользователи видят контент, к которому они не должны иметь доступа.
Чтобы предотвратить это, следует добавить корректные заголовки управления кэшем:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new NoCacheAttribute());
}
}
public class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuting(filterContext);
}
}
Вы также можете рассмотреть возможность реализации пользовательского атрибута авторизации, чтобы проверить валидность сессии:
public class SessionTimeoutAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
// Проверяем, что сессия пользователя всё ещё валидна
var authManager = httpContext.GetOwinContext().Authentication;
var authProperties = authManager.User?.Identity?.AuthenticationType;
return authManager.User?.Identity?.IsAuthenticated == true;
}
}
Полный пример реализации
Ниже приведена полная реализация, которая корректно обрабатывает таймаут сессии:
1. Конфигурация Web.config:
<configuration>
<appSettings>
<add key="SessionTimeout" value="30" />
</appSettings>
<system.web>
<!-- Это влияет только на состояние сессии ASP.NET, а не на OWIN Identity -->
<sessionState mode="InProc" timeout="30" />
<authentication mode="None" />
</system.web>
</configuration>
2. Конфигурация Startup.cs:
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
var timeoutInMinutes = int.Parse(ConfigurationManager.AppSettings["SessionTimeout"]);
// Пользовательский провайдер для обработки истечения сессии
CookieAuthenticationProvider provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(5),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => int.Parse(id.GetUserId())
),
OnApplyRedirect = context =>
{
if (context.Request.QueryString["session_expired"] != null)
{
context.Response.Headers.Add("X-Session-Expired", new[] { "true" });
}
context.RedirectUri = $"{new PathString("/Account/Login")}";
context.Response.Redirect(context.RedirectUri);
}
};
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = TimeSpan.FromMinutes(timeoutInMinutes),
SlidingExpiration = false, // Установить в false для фиксированного истечения
Provider = provider,
CookieHttpOnly = true,
CookieSecure = CookieSecureOption.None // В продакшене используйте SameSite=Strict
});
}
}
3. Account/Login.cshtml с уведомлением о таймауте:
public class AccountController : Controller
{
[HttpGet]
[AllowAnonymous]
public ActionResult Login(string returnUrl, bool session_expired = false)
{
ViewBag.ReturnUrl = returnUrl;
ViewBag.SessionExpired = session_expired;
if (session_expired)
{
ViewBag.Message = "Ваша сессия истекла. Пожалуйста, войдите снова.";
}
return View();
}
}
4. Глобальный фильтр для управления кэшем:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new NoCacheAttribute());
filters.Add(new SessionTimeoutFilter());
}
}
public class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuting(filterContext);
}
}
Устранение распространенных проблем
Проблема 1: Таймаут не работает
Если таймаут сессии не срабатывает:
-
Проверьте расположение конфигурации: Убедитесь, что
CookieAuthenticationOptionsнастроены в правильном файлеStartup.csи что классStartupкорректно указан вweb.config:xml<appSettings> <add key="owin:AppStartup" value="YourNamespace.Startup, YourAssembly" /> </appSettings> -
Проверьте ExpireTimeSpan: Убедитесь, что
ExpireTimeSpanустановлен правильно. Согласно обсуждениям Stack Overflow, многие разработчики случайно оставляют значения по умолчанию. -
Проверьте наличие нескольких конфигураций аутентификации: Убедитесь, что
UseCookieAuthenticationне вызывается несколько раз, так как последний вызов переопределит предыдущие настройки.
Проблема 2: Пользователи выходят слишком быстро
Если пользователи выходят из системы намного раньше, чем настроенный таймаут:
-
Настройка SlidingExpiration: Убедитесь, что
SlidingExpirationустановлен вfalse, если вы хотите фиксированное истечение, или вtrue, если хотите сдвигающийся таймаут. -
Валидация Security Stamp: Колбэк
OnValidateIdentityможет вызывать более частую проверку. Рассмотрите увеличениеvalidateInterval:csharpOnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>( validateInterval: TimeSpan.FromMinutes(30), // Менее частая проверка regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager), getUserIdCallback: (id) => int.Parse(id.GetUserId()) )
Проблема 3: Confusion между Session State и OWIN Identity
Если вы путаете различия между состоянием сессии ASP.NET и аутентификацией OWIN Identity:
- Состояние сессии ASP.NET: Управляется
sessionStateвweb.config, используется для хранения данных приложения на уровне сессии пользователя. - Аутентификация OWIN Identity: Управляется через
CookieAuthenticationOptions, контролирует, когда пользователь выходит из системы Identity.
Это отдельные системы, которые могут работать совместно, но имеют разные механизмы таймаута. Обычно рекомендуется настроить оба для оптимальной безопасности и функциональности.
Источники
- Stack Overflow - How to implement session timeout in .NET Framework 4.7.2 application
- Optimizely Blog - Make OWIN PCI Compliant using cookie authentication timeouts validateinterval vs expiretimespan
- Stack Overflow - ASP.NET Identity Session Timeout
- Stack Overflow - How to set TimeOut for OwinContext in MVC 5
- Stack Overflow - Timeout ignored by AspNet Identity and OWIN
Вывод
Реализация корректного таймаута сессии в .NET Framework 4.7.2 с использованием .NET Identity и OWIN требует настройки свойства ExpireTimeSpan в CookieAuthenticationOptions, а не полагаться на настройки sessionState в web.config. Ключевые выводы:
- Используйте
ExpireTimeSpan = TimeSpan.FromMinutes(X)вStartup.csдля установки фактической длительности таймаута. - Рассмотрите установку
SlidingExpiration = falseдля требований безопасности, например, PCI. - Добавьте правильные заголовки управления кэшем, чтобы предотвратить отдачу закешированных страниц после истечения сессии.
- Настройте поведение перенаправления, чтобы информировать пользователей о том, что их сессия истекла.
- Тестируйте как аутентифицированные, так и неаутентифицированные сценарии, чтобы убедиться, что таймаут работает как ожидается.
Следуя этим рекомендациям, вы сможете реализовать безопасный и эффективный таймаут сессии, соответствующий рекомендациям OWASP и обеспечивающий хороший пользовательский опыт в ваших приложениях .NET Framework 4.7.2.