I'm trying to set up SSO on two separate IIS web sites (with a common domain) using Forms Authentication. I'm using the common approach of setting the auth cookie to the common domain and using matching machine key to enable decryption.
This works with no issues on test sites that I created. However, when trying to implement this in a legacy Web Forms site, I'm running into something that I don't understand.
When I log into one site, the test code on the second site has the following results:
var cookie = FormsAuthentication.GetAuthCookie("username", false); // works
var ft = FormsAuthentication.Decrypt(cookie.Value); // returns correct info
var isAuthentication = User.Identity.IsAuthenticated; // false
As an added bonus, whenever I sign in one site the other gets signed out (happens both ways.)
There must be something fundamental that I'm missing here.
Why is User.Identity.IsAuthenticated set to false, even though the FormsAuthentication ticket seems to be decrypting with no issues?
UPDATE: as pointed out below - FormsAuthentication.GetAuthCookie is not a valid way to obtain the existing auth cookie. Which brings me to this: I can see the top domain cookie in the browser, but it doesn't show up in the request. I suspect this is where the problem is occuring.
Solution: Both sites were not targeting the same version of .Net Framework as specified by the web.config:
<httpRuntime targetFramework="4.5" />
<compilation debug="true" targetFramework="4.5" />
Updating both sites to target the same framework fixed the problem.
The GetAuthCookie creates a new cookie:
http://msdn.microsoft.com/en-us/library/vstudio/3fay1e4k(v=vs.100).aspx
Creates an authentication cookie for a given user name. This does not set the cookie as part of the outgoing response, so that an application can have more control over how the cookie is issued.
No wonder it works, it doesn't look into the existing cookie.
My theory is that you have your new sites on an x64 machine and the legacy website sits in an x86. The encryption differs in such scenario even if keys are the same. Another possible reason is a different version of .net as the encryption algorithm has been changed in .net 4.
Related
This has been the bane of my existence for the better part of a week.
I have four existing webforms applications that utilize forms authentication. The URL for each is mydomain/app1/, mydomain/app2/, etc. I have been tasked with creating a new application that will function as a single sign-on application with the URL mydomain/ssoapp. Once a user logs in, it basically compiles everything from the pre-existing apps that the user has access to, so our users don't have to go out and log into each of them separately. But the old applications need to function as they currently do.
The important part of my web.config is as follows:
<authentication mode="Forms" >
<forms loginUrl="frmLogin.aspx?Type=login" name="sqlAuthCookie" protection="All" path="/" domain="mydomain"
timeout="60" cookieless="UseCookies" enableCrossAppRedirects="true" />
</authentication>
<machineKey validation="SHA1" decryption="AES" decryptionKey="mykey" validationKey="myvalkey"/>
Simply adding this to the web.config for all of the applications worked like a charm....for three of them.
In the SSO application I'm creating a formsauthenticationticket, cookie, and adding that to the response with the following code. Each of the four pre-existing applications uses this same code as well:
Dim lTicket As New FormsAuthenticationTicket( _
1, _
pstrUserId.ToString, _
System.DateTime.Now, _
System.DateTime.Now.AddMinutes(60), _
True, _
pstrUserId.ToString, _
FormsAuthentication.FormsCookiePath)
' Encrypt the ticket.
Dim lencTicket As String = FormsAuthentication.Encrypt(lTicket)
' Create the cookie and add to response
Dim cookie As HttpCookie = New HttpCookie(FormsAuthentication.FormsCookieName, lencTicket)
cookie.Domain = ".mydomain.gov"
pobjResponse.SetCookie(cookie)
pobjResponse.Cookies.Add(cookie)
'Cleanup
lencTicket = Nothing
lTicket = Nothing
In chrome debugger for the SSO application, I log in and the cookie is created with the correct information.
I can click on my menu list, which uses a response.redirect to go out to the other applications. For the 3 working applications, I bypass the login screen, go directly to the form I need, and the cookie is unchanged
For the problem child application, I can still see the cookie however I am redirected back to the login screen.
If I login from this point, a new cookie is created, with the same name as the preexisting one, however the domain has the "www" prefix on it
Other useful information (maybe):
I've ensured that all machinekey, decryption key, validation method, etc match across applications
My domain is in the format of sub1.mid1.gov . I've tried every combination of the format for this in the cookie assignment and web.config. Both with and without the preceding dot.
I've removed httpRuntime from the web.config as some others had mentioned this causes issues.
There are no errors in the IIS logs
All applications are running under the same apppool currently
Currently I'm contemplating taking some vacation time so I don't feel bad about crying in the corner on my employers dime. I'm sure it's something ridiculously simple, but I appreciate any help in the matter. Thanks!
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.
I have my very own authentication system (https://bitbucket.org/anton_gogolev/octalforty-structural) which doesn't use any of the standard ASP.NET stuff (<authentication mode="None" />).
It uses plain IHttpModules to do its job: BeginRequest inspects incoming cookies and sets HttpContext.Current.User and Thread.CurrentPrincipal upon successful authentication
Thread.CurrentPrincipal = HttpContext.Current.User =
new GenericPrincipal(tokenIdentity,new string[] { });
whereas EndRequest issues all the required cookies for an authenticated user.
This has been working fine for months now, but on some systems (and I really cannot tell how are they different from ones this actually works on) ASP.NET seems to be losing the value of HttpContext.Current.User, replacing it with whatever default values there are (GenericPrincipal aggregating GenericIdentity with IsAuthenticated set to false, etc).
So the question is: how and why is HttpContext.Current.User getting lost?
It sounds like there is another module that is modifying HttpContext.Current.User after BeginRequest. I would recommend setting it instead in PostAuthenticateRequest.
I've had this problem before with ASP.NET enabling the RoleManager module. Adding the following to the system.web section of web.config fixed it.
<httpModules>
<remove name="RoleManager"/>
</httpModules>
Here's some amplifying information about what I did to fix this:
1) Figure out what other modules are running. Here's an article that provides some code for doing this.
2) Make sure you are setting HttpContext.Current.User in the correct place. BeginRequest isn't a good place to hook in. PostAuthenticateRequest is usually the best (and recommended). This won't prevent the problem if another module is also using PostAuthenticateRequest and it happens to be run after yours, but in many cases it will resolve the issue (use the web.config snippet above).
3) Selectively disable each installed module and test your application until your custom Principal object is not overwritten.
I use FormsAuthentication
I have a web farm with 2 nodes. What I do :
1) I log-in to my site by means of my factory through 1st node and go to default page.
2) I switch off 1st node, so next request should be to my 2nd node.
3) I'm trying to request some page(which should be available if I'm logged in) but application redirects me to login url. I think because of incorrect or missing cookies.
I used machine key
something like :
<machineKey
validationKey="C50B3C89CB21F4F1422FF158A5B42D0E8DB8CB5CDA1742572A487D9401E3400267682B202B746511891C1BAF47F8D25C07F6C39A104696DB51F17C529AD3CABE"
decryptionKey="8A9BE8FD67AF6979E7D20198CFEA50DD3D3799C77AF2B72F"
validation="SHA1" />
But It still do not work.
I am doing almost the same as described here :
http://msdn.microsoft.com/en-us/library/eb0zx8fc.aspx
But it just do not work for me.
What am I doing wrong ?
The problem was in security updates.
As soon as we installed updates the problem was solved.
Here is the list of possible updates which impact on this:
Security Update KB2656351
Security Update KB2487376
Security Update KB2633870
Security Update KB2572078
Security Update KB2518870
I have an ASP.NET 3.5 Web Site using the standard SQL Membership Provider.
The application has to pass the IBM Rational AppScan before we can push to production.
I am getting the error:
Severity: High
Test Type: Application
Vulnerable URL: http://mytestserver/myapp/login.aspx
Remediation Tasks: Do not accept externally created session identifiers
What can I do to fix this?
I am using SQL Membership Provider. Is this related? I am using the standard login controls too. I have the "Remember Me" turned off, and hidden.
Thanks.
This isn't a vulnerability (and I really don't like AppScan because of its false positives - the number of times I've had to explain CSRF cookies need not be linked to a session on my little open source project is getting annoying).
All that will happen in this case is the first time anything is stored in session state with a created session identifier a new session will be opened on the server, with nothing in it. If you're worried about session fixation then you can clear the cookie after authentication.
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
But with forms authentication the authentication details are not held in the session and so fixation is not a problem at all.
Frankly if you must pass security scans without anyone evaluating if the results are not false positives then that's a whole different problem.
You might need to change the default cookie settings to be unique to you app
Try setting a unique cookie path:
<forms name="YourAppName"
path="/FormsAuth" ... />
http://msdn.microsoft.com/en-us/library/ms998310.aspx#paght000012_additionalconsiderations
More reading...
http://msdn.microsoft.com/en-us/library/ms998258.aspx
It would seem RegenerateExpiredSessionId property is controlling this.
Do set it to true. Also keep time-out to a and low value, the tightest acceptable by users (e.g. 10 - 15 minutes).