IIS idle-timeout is not working - asp.net

I have set Idle-timeout to 1 minutes for the purpose of testing. In addition, I have a SessionState timeout is set to 3 minutes. SessionState timeout is working just fine, but IIS Idle-timeout is not working?
For your information, I have checked the file ApplicationHost.config, the setting is already there
<system.applicationHost>
<applicationPools>
<add name="dev_web_core" autoStart="true" startMode="AlwaysRunning">
<processModel idleTimeout="00:01:00" />
<recycling>
<periodicRestart time="00:00:00">
<schedule>
<clear />
<add value="01:00:00" />
</schedule>
</periodicRestart>
</recycling>
</add>
</applicationPools>
</system.applicationHost>

Based on your comment, it seems you expect the application pool idleTimeout to cause an authentication timeout. This is unrelated. This timeout triggers application pool recycling, which causes your application process to get stopped (and renewed if requests are coming).
Issued authentication tickets are unaffected by this process recycling (at least with frameworks like Asp.Net Identity, provided your site machine key does not change).
You should seek and setup the timeout of your authentication framework instead. With Owin by example, this can be setup when issuing the ticket.
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
// Your custom expiration, preferably taken from some settings
var expiration = rememberMe ? 144000 : 30;
var now = DateTimeOffset.UtcNow;
authenticationManager.SignIn(
new AuthenticationProperties
{
AllowRefresh = true,
IssuedUtc = now,
ExpiresUtc = now.AddMinutes(expiration),
IsPersistent = rememberMe
}, claimsIdentity);

Related

When I am deploying by ASP.NET MVC application on IIS I am specifying my credentials in Identity as a setting in DefaultAppPool

Also if i don't give my credentials it gives some configuration file error.
I want AD authentication to take place. But it always picks my username when logged from different systems. How do I resolve it?
Is there any way I can make a user group and assign that to the Identity in DefaultAppPool or do I need to change the code for authentication as it is only taking the user from the Identity mentioned in DefaultAppPool?
The code that I am using to validate the user who logs in is as follows:
string groupName = ConfigurationManager.AppSettings["groupName"].ToString();
string domainName = ConfigurationManager.AppSettings["domain"].ToString();
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, domainName);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx,IdentityType.SamAccountName,System.Security.Principal.WindowsIdentity.GetCurrent().Name);
if (user != null)
{ //code as required }
code in web.config file:
<appSettings>
<add key="domain" value="EYDEV" />
<add key="groupName" value="Domain Users" />
</appSettings>
WindowsIdentity.GetCurrent() gets the credentials that your application is running under, not the current user.
You need to first enable Windows Authentication by adding this to your system.web section of your web.config:
<authentication mode="Windows" />
See Microsoft's documentation for more details (like making sure you have the right IIS features installed): https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/security/authenticating-users-with-windows-authentication-cs
Once that is setup, you can use User.Identity.Name in a Controller to get the username of the current user.

Increasing session timeout to a week or more

In order to increase session timeout, it appears I would use the following setting:
<system.web>
<sessionState mode="InProc" timeout="20" />
/* Etc... */
</system.web>
Here the timeout is set to 20 minutes (the default value). And, apparently, the maximum value is 525,600 minutes, or one year.
I can come back to Facebook a week later and I'm still logged in. This is how I want my application to behave. But according to this answer, this can adversely affect performance because "your inactive sessions will remain in Web server memory which may cause application pool to recycle, which would result in loosing all sessions for all users."
Does anyone know the details about this performance hit? And, if it's for real, is there a more performant way to keep users logged in like sites such as Facebook?
UPDATE:
Below is the relevant section of my current web.config file.
<system.web>
<authentication mode="None" />
<sessionState mode="InProc" timeout="60" />
<compilation debug="true" targetFramework="4.6" />
<httpRuntime targetFramework="4.5.2" executionTimeout="240" maxRequestLength="20480" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
<customErrors mode="Off"></customErrors>
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="20971520" />
</requestFiltering>
</security>
</system.webServer>
UPDATE 2:
Looks like I was incorrectly conflating two issues (authentication and session state). I apologize for not correctly sorting through some issues I was Googling on. My goal is only to extended the length of time a user is logged in.
For Login, you must use FormsAuthentication or ASP.NET Identity (Improved version of cookie based authentication over FormsAuthentication) which allows you to keep authentication cookie for more then weeks/months. FormsAuthentication is stateless, and in order to support multiple servers, you can use single machineKey in all servers. All samples and tutorials mostly guide to use FormsAuthentication by default.
Faceboook and everyone use authentication cookie, no body uses Session for login.
Ideally Session is bad and mostly unnecessary. It can be replaced with HttpRuntime.Cache. Cache can be easily setup to use some external provider such as Fabric cache or Redis. To make cache isolated by user, you can simply append keys of cached item with username.
UPDATE
There is no downside in using FormsAuthentication except that there is little CPU overhead required in decrypting cookie, but that can also be avoided by caching authentication ticket.
The only reason to support Session could be compatibility with old ASP application they might be supporting.
In the new ASP.NET MVC sample, they have configured cookie based authentication in code (in startup), which is not session. Though session is configured in web.config but as long as you don't want to store anything in session, you can completely disable it.
Created a stock MVC project from scratch with Individual User Account selected for Auth.
Startup.Auth.cs
public partial class Startup {
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app) {
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = TimeSpan.FromDays(7)//<-- I just added this.
});
//...code removed for brevity
}
}
// Summary:
// Controls how much time the cookie will remain valid from the point it is
// created. The expiration information is in the protected cookie ticket. Because
// of that an expired cookie will be ignored even if it is passed to the server
// after the browser should have purged it
public TimeSpan ExpireTimeSpan { get; set; }
Changed nothing else in the project and the default template provided everything needed.
UPDATE
Based on comments, you could always add it as a app setting in web.config and use ConfigurationManager to access it. That way it can be modified without having to recompile code.
var expireTimeSpan = TimeSpan.FromDays(7);//the default
var setting = ConfigurationManager.AppSettings["ApplicationCookieExpireTimeInDays"];
if (setting != null) {
var days = 0;
if (int.TryParse(setting, out days)) {
expireTimeSpan = TimeSpan.FromDays(days);
}
}
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider {
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
ExpireTimeSpan = expireTimeSpan
});
Where web.config would hold the setting.
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="ApplicationCookieExpireTimeInDays" value="14" />
</appSettings>
The answer you cite is partially true. It depends on where the session state is stored.
There should be no issue with increasing the session state when storing the session state in the SQL Server database. Also using Web Farms - which makes sense to cater for scalability.
From this article:
Storing Session State in a SQL Server Database
Storing session variables in the SQL server has the following
advantages:
Scalability: If you are looking for a highly scalable option to store
your session variables, the SQL Server option is for you. It is a much
more scalable option than the others. Web farm architecture can very
easily access the session variables because they are stores in an
independent database.
Reliability: Because the data is physically
persisted in a database, it is is more reliable than the other
options. It has the ability to survive server restarts.
Security:
SQL Server is more secure than the in-memory or state server option.
You can protect your data more easily by configuring SQL Server
security.
It's an old article, but these principles still apply.
There can be issues when using memory of the Web Server.
How does increasing the session timeout effect the application performance and why?
If you extend the duration of Sessions, any items held in session
variables will stay in memory on the server longer. Depending on how
busy your application is, and the type and number of items you
persisits as session variables, this may degrade performance.
copied in the typo from the quote.
This question also discusses the difference between session state and the use of cookies with FormsAuthentication.
Should I use Session State or FormAuthentication to keep track of a signed-in user?
So depending on what type of authentication you are using - you may go the cookie route, bearing in mind the user can delete the cookie from the browser and this will log them out.
This is another helpful link to the docs.
Securing Session State

How to set machineKey on Azure Website

I'm running an Azure Website. Whenever I deploy, everyone gets logged out because the machineKey changes.
I specified the machineKey in the web.config but this didn't solve the issue. I believe this is because Azure automatically overwrites the machineKey [1].
I've found a couple of similar questions here but the answers link to dead links.
So, what's the solution? Surely there's a way to keep users logged in regardless of deployments on Azure.
Try to reset the machine-key configuration section upon Application_Start:
protected void Application_Start()
{
// ...
var mksType = typeof(MachineKeySection);
var mksSection = ConfigurationManager.GetSection("system.web/machineKey") as MachineKeySection;
var resetMethod = mksType.GetMethod("Reset", BindingFlags.NonPublic | BindingFlags.Instance);
var newConfig = new MachineKeySection();
newConfig.ApplicationName = mksSection.ApplicationName;
newConfig.CompatibilityMode = mksSection.CompatibilityMode;
newConfig.DataProtectorType = mksSection.DataProtectorType;
newConfig.Validation = mksSection.Validation;
newConfig.ValidationKey = ConfigurationManager.AppSettings["MK_ValidationKey"];
newConfig.DecryptionKey = ConfigurationManager.AppSettings["MK_DecryptionKey"];
newConfig.Decryption = ConfigurationManager.AppSettings["MK_Decryption"]; // default: AES
newConfig.ValidationAlgorithm = ConfigurationManager.AppSettings["MK_ValidationAlgorithm"]; // default: SHA1
resetMethod.Invoke(mksSection, new object[] { newConfig });
}
The above assumes you set the appropriate values in the <appSettings> section:
<appSettings>
<add key="MK_ValidationKey" value="...08EB13BEC0E42B3F0F06B2C319B..." />
<add key="MK_DecryptionKey" value="...BB72FCE34A7B913DFC414E86BB5..." />
<add key="MK_Decryption" value="AES" />
<add key="MK_ValidationAlgorithm" value="SHA1" />
</appSettings>
But you can load your actual values from any configuration source you like.
If Azure is rewriting your machineKey, you can't do much about it, as it is part of their infrastructure. However, there are other methods.
Override FormsAuthentication
This should not be difficult as you can easily look up for source code of FormsAuthentication and create your own logic and replace MachineKey with your own key stored in web.config or in your database.
Custom Authentication Filter
The simplest way would be to create a filter and check, verify, encrypt decrypt cookies in your filter. You need to do this on OnAuthorization method and create new instance of IPrincipal and set IsAuthenticated to true if descryption was successful.
OAuth
Enable OAuth and create OAuthProvider. However you will need to host OAuthProvider on server that is in your control as that will need machineKey working.
Enable Third Party OAuth, if you enable OAuth with Google, Facebook etc, it will be easy as user will be redirected to OAuth provider and they will continue to login automatically and a new session will be established.
I had the same issue and in my case I was using the webdeploy to Azure wizard in VS13. I thought I was going crazy as I would set the machinekey in the web.config and then it would be changed on the deployed web.config to autogenerate. It is something in the webdeploy script/settings. My solution was to open the live azure site from within VS13 using the Server Explorer and then editing the web.config and saving changes. This preserved my settings with my supplied keys and all works fine.

IIS Express - temporarily impersonate the authenticated user

According to this document:
http://msdn.microsoft.com/en-us/library/ff647405.aspx
I've set the following in web.config
<authentication mode="Windows" />
<identity impersonate="false" />
And have also set this in the applicationhost.config for IIS Express 7.5
<anonymousAuthentication enabled="false" userName="" />
<windowsAuthentication enabled="true">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
But System.Threading.Thread.CurrentPrincipal.Identity still always equals the Windows identity of the authenticated user, i.e. not the account that IISExpress.exe is running under (my dev account).
To be clear, I'm logged-in as Account A and IIS Express runs as Account A, but I call my web service using Account B (setting the Credentials on HttpWebRequest) but the server-side code runs as Account B, i.e. the thread has this id and I can access network resources.
I'd like execution to occur as Account A (and on the prod server, as a service account) and only impersonate when I want it to.
Am I doing something wrong or is this area not perfectly implemented in IISX?
Thanks
Luke
Update 1
So, I thought I figured-out what was going on; see my answer below. The problem is that it seems to be working in reverse!
string n1 = System.Security.Principal.WindowsIdentity.GetCurrent().Name; // Runtime account.
string n2 = System.Threading.Thread.CurrentPrincipal.Identity.Name; // Calling account.
var winId = (System.Security.Principal.WindowsIdentity)HttpContext.Current.User.Identity;
System.Security.Principal.WindowsImpersonationContext ctx = null;
try
{
bool b = System.IO.File.Exists(#"d:\p\p.txt"); // true (!)
using (ctx = winId.Impersonate())
{
// Now impersonating. Access (local) resources using the identity of the authenticated user.
n1 = System.Security.Principal.WindowsIdentity.GetCurrent().Name; // Calling account.
n2 = System.Threading.Thread.CurrentPrincipal.Identity.Name; // Calling account.
b = System.IO.File.Exists(#"d:\p\p.txt"); // false (!)
}
...
The folder d:\p is set to only allow the calling account access, which is fine when tested in DOS but from my web service, it has access and I expect this is because the thread has the caller's security context, BEFORE I've begun impersonating!
Weirder still, when I do impersonate, I suddenly lose access to it!
I'm going to create a test project on a proper IIS 7.5 server and see if this is a bug in IIS Express.
Update 2
The problem with the Exists test has been half-solved. I removed rights to the folder but the file itself still had some rights, and the way .NET accesses files without traversing the folder means it could still access it.
Now I get
b == false // as expected.
...
b == false // unexpected, after impersonation I should be able to see this file.
I'd expect impersonation to give me access, it doesn't.
Update 3
I've given up. Impersonation doesn't work and I can only assume its a network policy or some undiscoverable hidden setting.
Got it. Sort of.
string n1 = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string n2 = System.Threading.Thread.CurrentPrincipal.Identity.Name;
n1 = the process identity
n2 = the caller's identity
The thread's security context has the caller's identity, which I didn't expect. I thought the thread would have the context of the process flowed to it, but this is clearly not how it works.
I now have an interesting situation that when I call .Impersonate on the callers WindowsIdentity, I still can't access a local file permissioned for the calling account, but I'll work that out and update my answer.
SEE UPDATE IN QUESTION

asp.net forms authentication timing out after 1 minute

I'm using ASP.NET MVC 3 with the Authorize attribute, but it keeps kicking me to the logon page after 1 minute, but even though my expiration is set to a very high value, it times out quickly. I check the cookie in my browser and its still there and not set to expire until about a month later, and it's set to be persistent, so I'm not sure why it keeps booting me. It only happens on my published location, locally it works just fine.
var ticket = new FormsAuthenticationTicket(username, true, 500000);
var encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
web.config:
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="7200" slidingExpiration="false"/>
</authentication>
Chances are the worker process is recycled meantime, Where do you store the sessions?
Making your sessions stored out of process may helps.

Resources