I am using forms authentication for an MVC website and I am having a problem adding Cookies, I am using an Encrypted Forms Authentication Ticket and adding it to the Cookies but when inspecting my cookies it is there (by name "AuthCookie") but the value is always null and the Expires date is always set to "01/01/0001 00:00"... here is my Login controller code:
[HttpPost]
public ActionResult Index(Login login, string returnUrl)
{
if (ModelState.IsValid)
try
{
User user = UserManager.Login(login.Username, login.Password);
string serialUser = Serialize.SerializeToString(user);
string ticket = FormsAuthentication.Encrypt(
new FormsAuthenticationTicket(1, login.Username, DateTime.Now, DateTime.Now.AddMinutes(20.0), login.RemeberMe, serialUser));
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, ticket) { Expires = DateTime.Now.AddMinutes(20) };
Response.Cookies.Add(cookie);
if (String.IsNullOrEmpty(returnUrl))
return RedirectToAction("Index", "Home");
else
return Redirect(returnUrl);
}
catch (LoginFailedException)
{
ModelState.AddModelError("", "Login failed: Invalid Username or Password.");
return View(login);
}
else
return View(login);
}
At first I assumed the encrypted string was not working due to the length but I have tested this by creating a simple test cooke and I am getting the same result.
Can anyone help
When you call Redirect() or RedirectToAction(), you're terminating the response so the cookies aren't sent to the client. Some solutions:
Use TempData to persist the information across the direct, writing the Cookie in the action you redirect to.
Take a look at the way Forms Authentication cookie information is written in the NerdDinner code on CodePlex.
As mentioned in the comments, you can persist role information in Session. The recommendation to store the role information in Session and retrieve from Roles if not found would work, but I'd start by using the membership system as-is and performance tuning later if you see that it's a problem, rather than assuming it will be.
Related
I have an old MVC system in use that a Whitehat company is complaining about after penetration testing.
The scenario is as follows:
User logs in with credentials.
User takes note of .ASPXAUTH and ASP.Net_SessionId cookies and their values
User logs off
User then uses F12 to add the cookies and their values
Navigate to a page that would normally not allow as user is not logged in, but it works OK when it should not
On logout I am deleting the cookies - that is not the problem.
The problem is that the 'values' remain within the server somehow and can be reused.
The code I use for logoff is as follows:
FormsAuthentication.SignOut();
// Drop all the information held in the session
Session.Clear();
Session.Abandon();
// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);
// clear session cookie
HttpCookie cookie2 = new HttpCookie("ASP.NET_SessionId", "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);
return RedirectToAction("Index", "Home");
I'm using session Id in HTTPsession in ASP.NET Core 5.0:
HttpContext.Session.SetString("CompanyID", Convert.ToString(session.CompanyID));
Do I need to encrypt my id or will it get encrypted internally?
My id is kind of sensitive one and I want to use that id further in application.
Is my approach safe or not?
My edited code for more clarification
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index([Bind] LoginModel loginModel)
{
SessionModels session = new SessionModels();
session = //getting data from database
HttpContext.Session.SetString("CompanyID",
Convert.ToString(session.CompanyID));
}
[HttpGet]
public IActionResult MyAction(InternalModel internal )
{
string strCompanyID= HttpContext.Session.GetString("CompanyID");
if(!string.IsNullOrEmpty(strCompanyID= ))
{
////
}
}
The session is stored serverside, the client gets a cookie with a random session ID. Session data is not shown to the user unless you display it somewher.
But:
My id is kind of sensitive one and I want to use that id further in application. My approach is safe or not?
No. Sounds like you're using security through obscurity. You need to verify on every request that does something with a CompanyID that the logged in user is allowed to access that company's resources.
I'm using the this tutorial to secure my Web-API calls with basic auth. Basically it checks if there is a auth header on the request and then proves this header against a database:
public static bool CheckPassword(string user, string password)
{
//Do a Database checkup
if(CheckDB(user,password)) {
//if true, set the principal
var identity = new GenericIdentity(user);
SetPrincipal(new GenericPrincipal(identity,null));
}
else {
//return 401...
}
}
private static void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
This is working fine. But it queries the database for every request I do. Even when I just request a JavaScript file.
I like to optimize this process and just call CheckDB() on the first request. All following request should not need another database request. Is it possible to save the Principal? I tried to check Thread.CurrentPrincipal but it seams to reinitialize on every request.
You have a couple of options:
If you have a simple topology with only a single machine handling requests for a relatively small number of users you could implement an in memory cache of usernames and passwords that you can use to efficiently validate subsequent calls. You could implement this using something like ConcurrentDictionary that keys off the username and gives the password, although there are security considerations to such an approach (do you really want passwords to be in memory all the time?).
Set a cookie after a username/password pair has been validated. The cookie could contain something like the timestamp after which the username/password should be revalidated, along with some kind of hash that's generated by a means that only the server knows (and that it can use to validate that it set the timestamp).
With either of these approaches the "CheckPassword" method would either compare the password with the one from the cache, or checking the cookie, and if the result is satisfactory directly create a new principal without calling the database.
I'm trying to persist the ASP.NET_SessionId cookie value (for auditing purposes) and find that the same value is being used across sessions. For example, while in development, when I hit 'F5', I've started a new session, right? But I'm seeing the same value being displayed.
Another (more real world) example, I'm logged in; make and note of the cookie value; and then log out (fires the code below) but still see the same cookie value.
public ActionResult LogOut()
{
FormsAuth.SignOut();
Session.RemoveAll();
return RedirectToAction("Index", "Home");
}
thx
I have noticed that if a user is still logged in or has a persistent cookie, even if he gets "banned", or disabled in the database (Users Table flags), the user can still access everything until that cookie goes away or the user logs out of the site. Great security right.
So I am putting together a ActionFilterAttribute that checks for this, the disturbing thing for me is I have to hit the database for every controller that his ActionFilterAttribute is applied to. There has to be a better way of doing this but I have not found one yet.
Any ideas would be awesome..
There has to be a better way of doing this but I have not found one yet.
No there isn't. Sorry. If the notion of disabled/banned user exists only in your database there is no other way but hitting your database. ASP.NET only verifies the validity of the authentication cookie which is sent on each request. It doesn't even know what a disabled user means so you cannot expect it do more than it already does.
There are a few options:
1) You can validate whether the user authentication is valid by hooking session start. This way if the user has a persistent cookie, you can validate the username and expire the cookie if needed.
2) You can use a time based mechanism to check the user auth status every few requests (every 5mins or whatever). You could store the lastChecked timestamp value in the user session or in the auth cookie itself using the UserData field. This allows you recheck if the user auth cookie needs to be expired more frequently, but keeps database calls to a minimum.
MyThis is the solution I came up with:
In the User Account Membership service add a function to return whether the user's account is still active.
public class UserAccountMembershipService : IMembershipService
{
public bool UserIsActive(Guid userId)
{
if (userId == new Guid()) throw new ArgumentException("Value cannot be null or empty.", "userName");
MembershipUser user = _provider.GetUser(userId, true);
return user.IsApproved;
}
}
Override the AuthorizeAttribute as follows:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
IMembershipService membershipService = new UserAccountMembershipService();
//Check to see if the user's account is still active
bool isActive = false;
if (httpContext.User.Identity.IsAuthenticated)
{
Guid userId = (Guid)Membership.GetUser(httpContext.User.Identity.Name).ProviderUserKey;
isActive = membershipService.UserIsActive(userId);
}
if (!isActive)
{
//If the user's account is no longer active log him/her out
IFormsAuthenticationService FormsService = new FormsAuthenticationService();
FormsService.SignOut();
}
//Call the base AuthorizationCore method
return base.AuthorizeCore(httpContext) && isActive;
}
}