I have logout handler which used to work fine:
public void ProcessRequest(HttpContext context)
{
//// Sign out
System.Web.Security.FormsAuthentication.SignOut();
//// Clear Session
if (context.Session != null)
{
context.Session.Clear();
}
/// Expire all the cookies so browser visits us as a brand new user
List<string> cookiesToClear = new List<string>();
foreach (string cookieName in context.Request.Cookies)
{
HttpCookie cookie = context.Request.Cookies[cookieName];
cookiesToClear.Add(cookie.Name);
}
foreach (string name in cookiesToClear)
{
HttpCookie cookie = new HttpCookie(name, string.Empty);
cookie.Expires = DateTime.Today.AddYears(-1);
context.Response.Cookies.Set(cookie);
}
context.Response.Redirect("~/default.aspx");
}
}
Once I added "domain" parameter to the authentication section of web.config:
<forms timeout="50000000"
loginUrl="~/login"
domain='mysite.com'/>
... it is no longer logging the user out - after it redirects to "~/default.aspx" I can still see the user logged in (I put a breakpoint to Load event of that page and check HttpContext.Current.User.Identity.IsAuthenticated, and its still = true).
Then I remove "domain='mysite.com'" and it logs the user out without problems.
I do need to specify the domain because I added a subdomain with its own application but I want it to share authentication cookie.
Any ideas are highly appreciated!
When I recreate cookies to expire, I need to specify the domain:
cookie.Domain = FormsAuthentication.CookieDomain;
That solves the problem.
Please specify domain =".mysite.com"
Related
I am using authorize attribute over an action.
[Authorize(Users= "admin" )]
[HttpGet]
public JsonResult GetServices()
{
return Json(ServicesRepository.SelectServices(), JsonRequestBehavior.AllowGet);
}
While successfully logging in I am setting:
Session["Users"] = usersModels;
Session["UHTUserName"] = usersModels.UserName;
FormsAuthentication.SetAuthCookie(usersModels.UserName, LoginVM.RememberMe);
AuthorizeAttribute aattr = new AuthorizeAttribute();
aattr.Users = usersModels.UserName;
but still, it fails to authorize.
Based on the above code snippet, you are using form authentication with MVC.
When Forms authentication is being used, whenever the need for authentication arises, the ASP.NET framework checks with the current IPrinciple type object. The user ID and Role contained in this IPrinciple type object will determine whether the user is allowed access or not.
So far you have not written code to push your user's Role details in this principle object. To do that you need to override a method called FormsAuthentication_OnAuthenticate in global.asax. This method is called each time ASP.NET framework tries to check authentication and authorization with respect to the current Principle.
What you need to do now is to override this method. Check for the authentication ticket (since the user has already been validated and the ticket was created) and then supply this User/Role information in the IPrinciple type object. To keep it simple, you will simply create a GenericPriciple object and set user specific details into it, as follows:
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
e.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Note: In MVC 4 and later versions, this event will not work. To make the custom forms authentication work in MVC 4 and later versions, we need to put this code in Application_PostAuthenticateRequest event in the Global.asax file.
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
string roles = string.Empty;
using (userDbEntities entities = new userDbEntities())
{
User user = entities.Users.SingleOrDefault(u => u.username == username);
roles = user.Roles;
}
//let us extract the roles from our own custom cookie
//Let us set the Pricipal with our user specific details
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
Reference: https://www.codeproject.com/Articles/578374/AplusBeginner-splusTutorialplusonplusCustomplusF
Did you set the settings in web.config for Forms Authentication
<system.web>
<authentication mode="Forms"></authentication>
<system.web>
And while login set the cookie as follows
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, //version
UserName, // user name
DateTime.Now, // create time
expiration, // expire time
RememberMe, // persistent
strUserData); // user data/role
HttpCookie objHttpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
objHttpCookie.Path = FormsAuthentication.FormsCookiePath;
Response.Cookies.Add(objHttpCookie);
Fortify tool is showing the Header manipulation cookies issue on the below code.
HttpCookie cookieexit= new HttpCookie("CheckUserNameExists");
cookieexit.Value = userName;
cookieexit.Secure = true;
Response.Cookies.Add(cookieexit);
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, false));
Any help ?
It is not directly related to original question.
If you use FormAuthentication, it is not a good practice to create custom cookie name to keep track of logged-in username.
Instead, you want to use same cookie name FormsAuthentication.FormsCookieName that FormsAuthentication uses.
So, you replace 5 lines of codes with the following single line -
FormsAuthentication.RedirectFromLoginPage(userName, false);
Global.asax.cs
You then retrieve the username from cookie, and save it in both Principal Object and Current Thread.
public class Global : HttpApplication
{
private void Application_AuthenticateRequest(object sender, EventArgs e)
{
HttpCookie decryptedCookie =
Context.Request.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(decryptedCookie.Value);
var identity = new GenericIdentity(ticket.Name);
var principal = new GenericPrincipal(identity, null);
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = HttpContext.Current.User;
}
}
You can then retrieve username literally anywhere in your code as long as HttpContext.Current is available. For example,
string userName = User.Identity.Name;
Fortify is complaining because it thinks that userName could have dangerous characters, such as newlines, which could (maybe) allow header manipulation attacks in the scenario of social engineering/phishing attacks.
I'm not too sure of the practicality of such attacks, but I am pretty sure that if you validate that userName only contains a whitelisted set of characters that exclude any whitespace, then such an attack is prevented.
A user had access to a .net MVC OWIN authenticated website and has a cookie that logs them in. The administrator then revokes access and resets their role to a No Access role.
How can the site detect this? Can the Cookie authentication code be updated to check only the Roles for the user in the database upon logging in? If so, can someone point me in the right direction to override this function?
UPDATE:
I beleive that the code below in the OWIN Startup class is what needs to be updated to intercept/override the cookie authenticaiton. What I am trying to do is to check if the user has access to the site (if the user is part of a No Access role, deny them the login).
public partial class Startup
{
public void ConfigureAuthentication(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = context =>
{
//Somehow check the users role here?
}
}
});
}
}
Here is what I ended up doing which appears to work the way I think it should work. When the OWIN cookie is read, I check the users database (I realize that this is redundant) to ensure that their access has not been revoked. If it has been revoked, I reject the cookie authentication and they are sent back to the site login screen.
public partial class Startup
{
public void ConfigureAuthentication(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = context =>
{
string userName = context.Identity.Name;
User user = userRepository.Users.SingleOrDefault(u => u.UserName == userName);
if(user.Roles.SingleOrDefault(r => r.Name == "No Access") != null)
{
context.RejectIdentity();
}
return System.Threading.Tasks.Task.FromResult(0);
}
}
});
}
}
From my testing, this appears to work. I think there is probably a better, less redundant solution to this, but I haven't come across it. I believe that this code runs with every page load which is what worries me a bit. I'll probably post another question later asking if there is a better way to do this, but for now, this works.
Mark C's response really helped me tailor my search on this subject, thanks.
The simplest solution is to "delete" the user's cookies so that they are logged out. Then when they log in again, they'll have the new permissions.
I say "delete", because you don't actually delete the cookie, but make it invalid - typically by setting it's date in the past.
This code from the MSDN will set the expiry date of the cookie to one day in the past:
if (Request.Cookies["UserSettings"] != null)
{
HttpCookie myCookie = new HttpCookie("UserSettings");
myCookie.Expires = DateTime.Now.AddDays(-1d);
Response.Cookies.Add(myCookie);
}
Source
I'm trying to remove specific Set-Cookie header from HttpResponseHeaders in OnActionExecuted method of ActionFilter.
I'm having few issues with that:
I cannot see the way of enumerate headers. The collection is always
empty, even if I see headers in debugger.
Because I cannot
enumerate, I cannot remove specific header. I can only remove all
headers with the same key, but Set-Cookie can have multiple
entries.
Currently I'm removing all cookies, but this is not what I want.
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
HttpResponseHeaders headers = actionExecutedContext.Response.Headers;
IEnumerable<string> values;
if (headers.TryGetValues("Set-Cookie", out values))
{
actionExecutedContext.Response.Headers.Remove("Set-Cookie");
}
base.OnActionExecuted(actionExecutedContext);
}
From the link:
You cannot directly delete a cookie on a user's computer. However, you can direct the user's browser to delete the cookie by setting the cookie's expiration date to a past date. The next time a user makes a request to a page within the domain or path that set the cookie, the browser will determine that the cookie has expired and remove it.
So, how to remove/delete cookie in ASP.NET Web Api at action filter level, just try to set expiration date of cookie to a past date:
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var response = actionExecutedContext.Response;
var request = actionExecutedContext.Request;
var currentCookie = request.Headers.GetCookies("yourCookieName").FirstOrDefault();
if (currentCookie != null)
{
var cookie = new CookieHeaderValue("yourCookieName", "")
{
Expires = DateTimeOffset.Now.AddDays(-1),
Domain = currentCookie.Domain,
Path = currentCookie.Path
};
response.Headers.AddCookies(new[] { cookie });
}
base.OnActionExecuted(actionExecutedContext);
}
Because of the requirements of my project, I'm wanting to provide custom authenticate for my MVC controller actions. Therefore, I will not be using SetAuthCookie().
Intially I set a cookie as follows;
string userData = EncDec.MakeString(user.Email + "|" + user.UserId);
//the Cookie and FormsAuthenticationTicket expiration date/time is the same
DateTime cookieExpiry = DateTime.Now.AddMinutes(AccountPage.MvcApplication.COOKIE_EXPIRY_MINUTES);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // ticket version
user.UserName, // authenticated username
DateTime.Now, // issueDate
cookieExpiry, // expiryDate
false, // true to persist across browser sessions
userData, // can be used to store additional user data
FormsAuthentication.FormsCookiePath); // the path for the cookie
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
//create the cookie
HttpCookie cookie = new HttpCookie("ADV_" + Extensions.ControllerExtensionMethods.GetGuid(this), encryptedTicket);
cookie.Secure = true;
cookie.HttpOnly = true;
cookie.Expires = cookieExpiry;
Response.Cookies.Add(cookie);
The HttpCookie is being saved in the client browser with a encrypted FormsAuthenticationTicket.
Then within my controller actions, whenever I need to check and verify that the user is authenticated I call this method;
public static FormsAuthenticationTicket IsAuthenticated(string guid)
{
HttpCookie cookie = HttpContext.Current.Request.Cookies["ADV_" + guid];
if (cookie != null)
{
string encryptedTicket = cookie.Value;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encryptedTicket);
if (!ticket.Expired)
{
//if the user is authenticated and the cookie hasn't expired we increase the expiry of the cookie - keep alive
DateTime cookieExpiry = DateTime.Now.AddMinutes(AccountPage.MvcApplication.COOKIE_EXPIRY_MINUTES);
//create a new ticket based on the existing one
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
ticket.Version, // ticket version
ticket.Name, // authenticated username
ticket.IssueDate, // issueDate
cookieExpiry, // expiryDate, changed to keep alive if user is navigating around site
false, // true to persist across browser sessions
ticket.UserData, // can be used to store additional user data
ticket.CookiePath); // the path for the cookie
string newEncryptedTicket = FormsAuthentication.Encrypt(newTicket);
//keep alive
HttpCookie newCookie = new HttpCookie("ADV_" + guid, newEncryptedTicket);
newCookie.Secure = true;
newCookie.HttpOnly = true;
newCookie.Expires = cookieExpiry;
HttpContext.Current.Response.Cookies.Set(newCookie);
return newTicket;
}
}
return null;
}
Every time the user is re-authenticated, I am increasing the time out of when the cookie will expire, so that the login is keep alive.
Everything seems to work fine, and the users are correctly authenticated, and if they aren't authenticated I redirect them to a login page, and they can't access methods if they aren't authenticated either.
My questions are:
Is this way of dealing with the authentication secure.
Is there anything I should be aware of, in terms of a security risk.
Thanks.
You basically need to look at creating a Custom Authentication Attribute.
I'm not going to provide the actual implementation here, but this will put you on the right path.
Here's the basic representation :-
public class GoogleAuthAttribute : FilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
IIdentity ident = filterContext.Principal.Identity;
if (!ident.IsAuthenticated || !ident.Name.EndsWith("#google.com"))
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
if (filterContext.Result == null || filterContext.Result is HttpUnauthorizedResult)
{
filterContext.Result =
new RedirectToRouteResult(new RouteValueDictionary
{
{"controller", "GoogleAccount"},
{"action", "Login"},
{"returnUrl", filterContext.HttpContext.Request.RawUrl}
});
}
}
}
I've took this from the Apress Book i'm currently reading on MVC5. If OnAuthentification fails, you set the Result property of the AuthenticationContext, this is then passed to the AuthenticationChallengeContext, where you can add your challenge code.
In my example, the user is redirected to the login page.
All you need t do, is place this AuthentificationAttribute on the Action Methods you require.
You should be able to build in or work your custome security code in to this.
You should really ask yourself if its a good idea to be adding custom security measures, as it can lead to more problems that you want.