I have a basic Authentication system on my Asp.net MVC Website
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
WebSecurity.Login(model.UserName, model.Password, persistCookie: false)
return RedirectToAction("Index", "Home");
}
I also have a UserInfoViewModel class where i keep some user specific information, and i use it on different pages.
To avoid creating the UserInfoViewModel every time i need it, i want to save it in Session on Login method.
public ActionResult Login(LoginViewModel model, string returnUrl)
{
WebSecurity.Login(model.UserName, model.Password, persistCookie: false)
var userInfoViewModel = new UserInfoViewModel();
Session["userInfo"] = userInfoViewModel;
return RedirectToLocal(returnUrl);
}
Considering that i have sensitive information that i rely on inside UserInfoViewModel, like IsSuperuser, is it safe to keep that object in Session? Will it expire when the user login session expires as well?
SOLUTION
System.Security.Principal.IIdentity is exacly made for that. It saves inside AUTH cookie custom user information you need, so you don't recalculate it every time.
Use Custom Principal Objects video turorial
Thank you for answers!
Yes, it is safe because the Session is stored on the server. But you have another problem you should be thinking about if you decide to use ASP.NET Sessions. If this session is stored in the memory of the web server (default), IIS could recycle your application at any time and you will loose this session data. On the other hand the user will still be authenticated because he is tracked by a forms authentication cookie which will still be sent. So if you want to use Sessions I would recommend you switching to an out-of-proc session provider (such as StateServer or SQLServer).
Also as #Mikeb is pointing out in the comments section there's another very serious issue with the Session. If you enabled it for read and write mode for a given controller you will not be able to process multiple requests from the same session in parallel. The server will block and process them sequentially. Think for example multiple AJAX requests from the same session. They will all block and process sequentially.
Related
Running .net 4.6.1 and MVC 5 using the built in authentication (OWIN) and Identity I am having a problem with sessions occasionally not being preserved when returning from the third party payment provider.
I have spent some days on this reading, searching and testing, but although I see similar problems storing a session variable and then redirecting, those were all in older versions and I am not sure this is what is happening here. Furthermore it only happens about one in ten times and I have had it both fail and work in chrome so it is not browser specific.
There is a lot of code, but the relevant parts look like this:
[HttpPost]
public async Task<ActionResult> CreateUser(Model model)
{
// store number so I can find user later
Session["mobilenumber"] = model.mobile;
// create user (skipping code for populating user and password from model)
var result = await UserManager.CreateAsync(user, password);
if (result.Succeeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false); // TODO: persist and remember false?
Redirect("https://paymentprovider.com");
}
}
When returning I try to read the session:
string mobileNumber = (Session["mobilenumber"] ?? String.Empty).ToString();
I log the session ID and Session_Start and Session_End, and the log looks like this when it fails:
Time Thread Details
09:15:58 34 Error retrieving session details since those were stored in session ntzwxal2iryodl40pmjtp5re
09:15:58 34 User returns from payment provider and new session is started, Session_Start: xcgxslnc0qblcry0crjwoykp
09:14:59 41 Create, sign in and redirect user to payment provider (store user in sessionID: ntzwxal2iryodl40pmjtp5re)
09:13:20 41 Session_Start: ntzwxal2iryodl40pmjtp5re
I am at my wits end trying to figure out why it randomly uses a new session at times. Seems it must be a problem with the cookie, but note from the log above that the session was started long before the redirect, so I suspect that the problem is something else. I don't think the thread it runs on is interesting and haven't seen a pattern there, but include it anyway.
Note that most of the time it does work and gets the same session, but suddenly when testing I get this. This is in release running on IIS server.
Also not that I considered going cookieless, but that does not work with how the code is written, is not secure with authentication from what I read and creates ugly urls. So I'm stuck with cookies.
Note that User.Identity.GetUserId() also fails, which I think again just shows that the session is lost.
What is the different between the following 3 methods to retrieve the claim?
Called in a ApiController:
((ClaimsIdentity) HttpContext.Current.User.Identity).Claims
((ClaimsIdentity) Thread.CurrentPrincipal.Identity).Claims
((ClaimsIdentity) User.Identity).Claims
The first two attributes have stored the same data but the last one has stored the data from the previous session.
This is done in the logout method:
UserCache.Instance.Clear();
FederatedAuthentication.SessionAuthenticationModule.SignOut();
HttpContext.Current.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
Update
Mixed WebForms, WebApi, MVC Application
Most of the application is build using WebForms.
If you are working with WebApi, then HttpContext.Current should not be available directly (see this answer). So I'm guessing you are using MVC as well and you see MVC context there.
Thread.CurrentPrincipal is dangerous to use because it contains thread principle which can be something you never expect, like user that actually runs IIS (AppPool user). Most of the time it is what you think, but sometimes it is not. And this will cause you endless bug-chasing that you can never recreate yourself.
User.Identity as ClaimsIdentity is the correct way to get what you need and it is used in the default template from VS. However if you see the data from "previous session" - means your cookies are not cleared properly. And the way you sign-out user looks suspicious:
What is UserCache.Instance?
SignOut method does not actually sign out user until the request is complete. So if you call this and then check for user identity within the same request, you'll see the same identity intact.
Assigning HttpContext.Current.User will not give you much within the request. See very first point if we are talking about pure WebAPI.
Default sign-out is done via IAuthenticationManager
private IAuthenticationManager Authentication
{
get { return Request.GetOwinContext().Authentication; }
}
[Route("Logout")]
public IHttpActionResult Logout()
{
Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
return Ok();
}
Try this and then adjust for your needs.
I have a MVC 5 asp.net website where I need to expose a number of REST APIs to a stand-alone mobile client. The rest of the site is using Forms based security where it sets the ASP.NET_SessionId as a cookie, and that is used to authenticate the user with the request after they log in. With my mobile application, I am not able to use the cookie method because of the cross-doman issue. What I would like to do is add a header "X-SessionId" with the value of the ASP.NET_SessionId, then on the server side, have a filter that looks for that field, and if it is present, associates the request with the given session. (Client will log in with an AJAX POST call which will return the ASP.NET_SessionId upon successful login).
Is this possible?
Something like this?
public sealed class CustomSecurityAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["X-SessionId"]) && IsAuthenticated(ilterContext.HttpContext.Request.Headers["X-SessionId"]))
filterContext.Result = new HttpNotFoundResult();
}
private bool IsAuthenticated(string sessionId)
{
// get your user details from your database (or whatever)
var user = new UserRepository().Get(sessionId);
if (user == null)
return false;
// build up an identity, use your own or out of the box.
FormsIdentity itentity = new MyIdentity(user);
// Set the user
filterContext.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(itentity , user.Roles);
return true;
}
}
You are going to have to store current sessions in your database, so for example when a user logs in grab the sessionid and stick it in the db, so you know they have 1..n current sessions.
Then you can look it up as part of your authentication.
Edit:
Let's take a step back, never mind cookies and sessions for the moment.
You have a website and a restful api, they both servce different purposes and clients and have different security requirements.
So what are the most common options for securing your Api?
Basic authentication.
Most restful APIs require a username/password to be sent through with each request, as part of the headers or in the request itself.
An authentication token
You can provide a token associated with a user account (a guid could suffice) when requests are made you check for the token.
Using an existing protocal like OAuth
I would recommend using these common scenarios to be sure you don't miss something and open your self up to security vulnerabilities.
Is there a reason you can't use any of these?
Is there any good reason why ASP.NET's session state cookie and the Forms Authentication cookie are two separate cookies? What if I want to "tie" them to each other? Is it possible in an elegant way?
Right now, I am stuck with the following solution, which works, but is still ugly:
[Authorize]
public ActionResult SomeAction(SomeModel model)
{
// The following four lines must be included in *every* controller action
// that requires the user to be authenticated, defeating the purpose of
// having the Authorize attribute.
if (SomeStaticClass.WasSessionStateLost/*?*/) {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
// ...
}
#RPM1984: This is what happens:
[HttpPost]
public ActionResult Login(LoginModel loginModel)
{
if (/* user ok */)
{
// ...
Session["UserID"] = loginModel.UserID;
Session["Password"] = loginModel.Password;
// ...
}
else
{
return View();
}
}
And it doesn't take much guessing to know what WasSessionStateLost does.
Session != Authentication
The session state cookie tracks the user's activity during a browser session.
The forms authentication cookie tracks the user's authenticated activity during a given time period, specified by the expiration date of the ticket and whether or not you have created a persistent cookie (e.g "Remember Me" checkbox).
You shouldn't be touching the session cookie itself, and all it contains is an identifier to tie the client session (browser) to the server.
If you need to access the session, use HttpContext.Current.Session.
What exactly are you trying to "tie" together?
What does SomeStaticClass.WasSessionStateLost do?
I'll start with a solution, then an explanation followed by a recommendation.
Create a custom authorization attribute:
Since your application defines Authorized as follows:
Logged in
Must have values in Session["UserID"] and Session["Password"]
you need to define your own AuthorizationAttribute
public class AuthorizedWithSessionAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if(httpContext.Request.IsAuthenticated &&
Session["UserID"] != null && Session["Password"] != null)
return true;
// sign them out so they can log back in with the Password
if(httpContext.Request.IsAuthenticated)
FormsAuthentication.SignOut();
return false;
}
}
Replace all your [Authorize] attributes with [AuthorizedWithSession] and you shouldn't need to put session check code in your controllers.
I don't know enough about your application, but saving passwords in session (even worse in plain text) is not a secure thing to do.
In addition, as RPM1984 said, the session cookie and authentication cookie are separate.
Explanation:
Think of the session as a bucket of info (on the server side) with your name on it. ASP.NET can take and put stuff in that bucket. ASP.NET gives you a name, your session id, and puts it on the bucket so it can know which one is yours.
The authentication cookie tells ASP.NET that you're authenticated and stores your authentication name in it. The authentication name is usually set by the developer of the application and is usually a unique key (think primary key in a DB) to separate you from the other users.
Recommendation to be more secure:
Encrypt the passwords before your store them. This is not total security, but it beats storing passwords in plain text and of course, if someone were to get a hold of the encryption key, they can crack the passwords.
Rather than using session, which is short lived you could cache in the System.Web.Cache. With this you can add events that are called before an entry is removed and decide accordingly if the cache should be cleared. You can set a higher time-out value on that, with the added bonus that you're not storing the clear text password in a file or database anywhere. Another bonus is you won't be vulnerable to session hijacking.
Of course if the application pool recycles the cache is gone, and as it's in memory load balanced machines will be out of sync, but Velocity or another distributed, out of process cache system would solve that.
It's not perfect though, entries may be dumped due to pressure on the cache, and of course you know this is all a bad idea anyway, so I'll skip that lecture.
I am finishing a project that started another programmer. Alter the entire architecture is not possible, but some things I want to overwrite. Namely - the authorization, and a way to store the current user session.
The project represents a client that communicates with the server through the soap-services. On the server, there is Security-Services, and several others, for example, A-service, B-Service.
Security service provides authentication and session key with which initialized other services.
The project is written in ASP.NET MVC3, the head of a user model, which is implemented as singletone-Class, which describes the methods of interacting with services.
How authorization works - there is CustomMembershipProvider with overridden ValidateUser method, which operates on Security-service. If successful authorization occurs the user registration in asp.net - FormsService.SignIn (model.UserName, false) and then initizalied user class:
class SiteUser
{
public static SiteUser Current
{
get
{
if (HttpContext.Current.Session.IsNewSession & &! HttpContext.Current.User.Identity.IsAuthenticated)
{
throw new UserAutorizationExeption () {Reason = AuthExceptionReason.NewSession};
}
if (HttpContext.Current.Session [sessionKey] == null)
{
FormsAuthentication.SignOut ();
throw new UserAutorizationExeption () {Reason = AuthExceptionReason.ServerSessionExpired};
}
return HttpContext.Current.Session [sessionKey] as W1User;
}
set
{
if (HttpContext.Current.Session! = null)
{
HttpContext.Current.Session [sessionKey] = value;
}
}
}
public SiteUser ()
{
}
public static SiteUser Create ()
{
SiteUser.Current = new SiteUser ();
return SiteUser.Current;
}
/ / Web-services methods go here
}
The main problem is that now the session is stored in memory:
web.config:
<sessionState mode="InProc" timeout="20" />
Set SqlServer-mode is problematic because it would be difficult SiteUser serialized. How can I get around this?
And there are problems with authorization - how to correctly do Asp.Net synchronization sessions with a session on services?
Sorry for my English, if needed clarification - ask questions. Thank you.
I personally prefer things simpler hence if I could, I would have a dedicated user to use the services so you do not have to impersonate the user down to the services layer hence having to maintain a session key.
Having said that it is not always possible, especially in a SOA environment where service layer does provide services to 3rd parties and auditing, etc. In fact my project looks like this.
You cannot get away from having a session if you need to impersonate the user to the service layer. InProc session provides better performance and SqlServer mode provides better scalability - decision on trade off is yours.
There is an alternative to store user's session key in the user table itself and retrieve every time and invalidate when user logs out. But this is only a custom implementation of user session.