Forms Authentication SSO Logout Issue due to duplicate cookie - c#

I am using forms authentication to handle SSO across *.mydomain.com
Upon login i create a non-persistant cookie with the ticket embedded in it and set it's domain to .mydomain.com.
However the problem is that when i visit any website on the domain, i find there are two cookie with the same name but with different domains. :
1> site1.mydomain.com [1st Forms Auth Cookie] duplicate cookie
2> .mydomain.com[2nd Forms Auth cookie]
The 1st Cookie is not created by me.Also it isn't secure.
Upon logout i successfully clear my 2nd Cookie.
However, the 1st cookie remains and for a user it appears that he is still logged in to site1 . This happpens because i check for the existence of the cookie with the same name and surely the 1st cookie is wrongly assumed to be the Forms Auth Cookie. I cannot check the domain property because in the Request the information about the domain is null .
My issue is how is the second website-specific domain cookie is getting created.
If i cannot avoid this, is there a workaround?
UPDATE : The encrypted value stored by this faulty cookie is updated on each request i make on the website,while the encrypted value in my Auth Cookie remains same

According to MSFT Docs
"The Add method allows duplicate cookies in the cookie collection. Use the Set method to ensure the uniqueness of cookies in the cookie collection."
After renewing my Forms Authentication ticket i was doing :
Response.Cookies.Add(..);
Thus a new duplicate cookie was getting generated.
To fix this do :
Response.Cookies.Set(..);
If you were like me you would have expected ASP.Net to replace the old cookie with the new one.

Related

Antiforgery cookie in ASP.NET Core 2.0

I've been trying to understand exactly how the antiforgery works. What confuses me are the cookies which are created. To my understanding, you include the antiforgery token in your form and then you validate for that token when a request is made. This way if a third party websites posts to your website, it will be denied.
Now, I'm reading here https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-2.1 that the antiforgery token gets stored in a cookie, maybe I'm reading this wrong? But why? Isn't the whole point of this not to make this value accessible outside of your website? If I look at my cookies, I can see 3 cookies created with antiforgery in their name.
services.AddAntiforgery(options =>
{
options.CookieDomain = "contoso.com";
options.CookieName = "X-CSRF-TOKEN-COOKIENAME";
options.CookiePath = "Path";
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.RequireSsl = false;
options.SuppressXFrameOptionsHeader = false;
});
I did a little test, I created a post form which ended up including anti forgery token and then I tried submitting it and it worked. Then I created another form without the token and then it failed. So to me it seems it only looks for the token passed in the form, then what is the cookie for?
Anti-forgery is a two-part process. When the page is generated, the token is included as part of the form, so that it will be posted along with the rest of your data. The cookie is set, for the client side of things. When the post is made, the client sends the request with the post data (including the token) and it sends the cookie back to the server, which also includes the token. Server-side, the posted token is matched up with the cookie token, and rejected if the two don't match.
This may seem weird since the client is posting both, but the cookie part ensures that the same client that got the page is also the same client sending it back. The goal isn't so much to protect the anti-forgery token, but rather to ensure that the page on your site is the one that's submitted, rather than some scammer's recreated version of your page. Since a third-party would be incapable of setting a cookie for your domain, there's no way they can fake this portion of the check, even if they were able to retrieve a valid token from your page by requesting it and parsing out the token.
From the asp.net core website AntiforgeryOptions.Cookie Property. The cookie part of the CSRF is only necessary when using cookie based authentication.
The cookie used by the antiforgery system is part of a security system that is necessary when using cookie-based authentication.

Differentiate between expired session and new user in asp.net core 2.0

I'm using the standard Authentication middleware in my Startup.cs file like this:
services.AddAuthentication("auth")
.AddCookie("auth", options =>
{
options.LoginPath = new PathString("/account/index");
options.AccessDeniedPath = new PathString("/account/forbidden");
options.SlidingExpiration = true;
});
I want to be able to display a "Your session has expired" message on my login page once the user is redirected there because of an expired session. And obviously not have it there when a new user is visiting the page.
By the looks of it, the Authentication middleware simply checks for a 401 passed from another middleware and changes it to a 302 to my desired location, leaving me no chance of differentiating between those two.
Can I achieve this differentiation by using the standard .net core 2.0 libraries, or should I go for a custom implementation instead?
If you want to detect whether or not the user was logged in, you could set a second unrelated Session cookie saying that the user HAD a session. Do not store any other information in the cookie. Let's call this a 'Flag' cookie.
When the user logs in, you create both a session cookie and a flag cookie. The session cookie can expire whenever you set it to, and the flag cookie should never expire. Lets say after 30 days, you are automatically logged out of your account - the session cookie should expire. Because you still have the flag cookie but not the session cookie, you can check on each page if you have the flag cookie when no session cookie is present.
If the Flag cookie exists but the actual session cookie does not, you can redirect to the login page and inform them that their login session has expired. I'd be sure to delete the cookie once you get there too so that every time they load a page they aren't redirected to login. This means the message will only show once to the user so they can still try visiting other pages on the site afterwards without being redirected should they want to browse (for example the homepage) but not want to log back in. If they attempt to access an authenticated page, they should be prompted for credentials again

Invalid token for impersonation - it cannot be duplicated since IIS 7

I have a site where we setup auto-login with windows authentication and if it fails, it asks for username/password to login as form.
The way it's done, we setup the authorization as Form with a specific page (Winlogin.aspx).
Winlogin.aspx is setup with authorization Windows. In the code of Winlogin.aspx.cs, it gets the this.Request.ServerVariables["LOGON_USER"] and creates a FormsAuthenticationTicket with the user's token using request.GetUserToken(), sets a cookie with an encrypted version and sends the browser to the login.aspx page for the Form authorization.
Login.aspx get the cookie, decrypt it and is suppose to set HttpContext.Current.User with a WindowsIdentity created from the user that was sent from Winlog.aspx after authorization has succeeded.
This has been working perfectly on IIS6 for more then a year now
but
we are updating our servers and moving to IIS 7 but now i get a Invalid token for impersonation - it cannot be duplicated.
This is the code that is used
// Extract the roles from the cookie, and assign to our current principal, which is attached to the HttpContext.
FormsAuthenticationTicket winAuthTicket = FormsAuthentication.Decrypt(authCookie.Value);
String token = winAuthTicket.UserData;
IntPtr userToken = new IntPtr(int.Parse(token);
-----> Line that gives error now. <-----
WindowsIdentity identity = new WindowsIdentity(userToken, "NTLM", WindowsAccountType.Normal, true);
HttpContext.Current.User = new WindowsPrincipal(identity);
I have been at it for 2 days trying to figure it out.
Anyone has an idea?
To resolve this you have to use a WindowsIdentity from a token that was not duplicated. So you have to pass HttpContext.User a WindowsPrinciple that was passed a WindowsIdentity that was generated by an original token (not duplicated). I've been trying to figure out a way to bypass this, but it looks like the only way is to store the credentials rather than the identity and Logon (see LogonUser) each time you need a WindowsIdentity.
You are unable to re-use an original token because HttpContext.User is disposed after each request (for security reasons).
I think the problem is, that you cannot reuse the same instance of WindowsPrincipal in another request, because SafeHandle instance, that probably holds some references that are assigned to WindowsPrincipal, had been disposed with the previous request and also you cannot duplicate WindowsIdentity.Token - it has to be regenerated. You have to clone the WindowsIdentity - this will get rid of reference dependencies. Then you can regenerate the web site authentication token by WindowsIdentity.Token or whatever.
I had had the same problem and I have solved it this way.
var user = new WindowsPrincipal(((WindowsIdentity)HttpContext.Current.User.Identity).Clone());
//now assign the user again to the current context's user or do some other stuff like storing the clone to session for future requests...

.ASPXAUTH Cookie Not Found in Request.Cookies

I am creating a web application that is hosted under a web site with forms authentication enabled. I have a role in my authentication database "Admins". Here is my controller code:
[RequireHttps]
[Authorize(Roles = "Admins")]
public ActionResult Index()
{
return this.View();
}
When I go to the Index page, if I'm not authenticated, it redirects me to the login page where I enter my credentials. The login page then redirects back to Index page of the new app, but the controller doesn't recognize that the user is authenticated.
I have taken the Authorize attribute off and looked at the request as it went out in the Chrome developer console and confirmed that the cookie is indeed being sent. But if I leave the Authorize attribute as is, and go to the Index page, the cookie collection on the request in my controller is empty. The headers collection contains a header entitled "Cookie", and the value of the header contains the .ASPXAUTH cookie.
The login page calls logs in with this code:
FormsAuthentication.SetAuthCookie(userName, remember, "/");
This behavior is reproducible in all major browsers.
What can I do to cause the Cookies collection of the request to be populated?
What do I need to do to make the application realize that the user really is authenticated?
Edit:
I still don't have it working, but I'm pretty sure it's something to do with the ASPXAUTH cookie being filtered.
I'm sure there are multiple causes of this problem. In my case, the problem was that the version of MVC I was using to write the cookie was different from the version that was decrypting it. I changed my sites to all be running MVC 4, and the cookie that was created by one site was consumable by the other site.
Is the .ASPXAUTH cookie generated a secure cookie, i.e. SSL? If so and your Index.aspx is only over HTTP not HTTPS, you will not see the cookie in the collection.

FormsAuthentication authCookie is null only for some users

I am experiencing a strange problem with asp.net forms authentication. This problem only occurs for 3 users out of 30+ users that have successfully logged in. I am using very basic auth code that I have used many times and have never seen this problem. After the users successfully authenticates and the auth cookie is created, cookie added, and response.redirect to FormsAuthentication.GetRedirect(userid, false) is called. The Application_AuthenticateRequest method in Global.asax is hit.
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if (null == authCookie)
{
// There is no authentication cookie.
return;
}
So immediately after a "good" cookie is saved and the redirect occurs the cookie is null. I have run the code through the debugger and the cookie is only null on these 3 users. But the cookie looks the same as the cookie for the many users that login successfully.
Any ideas? This is standard code that should just work.
Are you sure the "good" cookie is saved and exits to the response? It is possible in FormsAuthentication for a good cookie to be added to the header but the response is killed on the way out by some other system error (w3wp.exe crashing for instance) so a new response is generated without the cookie and the redirect occurs anyway.
In my own experience with a problem similar to this, I had a custom Principal class that was crashing after authentication (and cookie creation) and instead of writing an appropriate cookie, removed the cookie from the response entirely.

Categories

Resources