Where should i store "MemberID"? - asp.net

In my webpage i use FormsAuthentication
FormsAuthentication.RedirectFromLoginPage(VisitorEmail, False)
Every time the visitor gets authenticated via the login page, i set the
Session("MemberID") = GetMemberIDByEmail(VisitorEmail) for later processing.
Since i need both MemberID and VisitorEmail.
But something tells me that this is "out of the book" and not "by the book".
So am i doing something WRONG or BAD here?

Sorry, I'm not sure exactly what you are trying to do from your description, but there's no need to store the MemberID in session state. Whenever you need it, just call:
Membership.GetUser.ProviderUserKey
Note: Its not really considered good form to store information in Session state as this could be lost e.g. if the web server resets - which it does periodically, or if the site needs to recompile. Also, its not very scalable as each "active" user will use up memory and also if you ever need to move to a web farm session state can cause issues as it will be different on each web server.
Prob OK for a little, quick site though ;-)

It's fine to use Session to cache this type of info, but remember to reassign it when the session expires in Global.asax:
void Session_Start(object sender, EventArgs e)
{
if(Request.IsAuthenticated) //to make sure the user has not logged out
Session["MemberID"] = GetMemberIDByEmail(VisitorEmail);
}

You could create a custom principal class so you can add the additional properties. Then modify your Global.asax to override Application_PostAuthenticateRequest with your code and also set Context.User = Thread.CurrentPrincipal = myPrincipal;. Best is to always set Thread.CurrentPrincipal, but normally you can also get to your own properties elsewhere in your code using the more "convenient" Page.User or Context.User.
Context.User vs. Thread.CurrentPrincipal / why FormsAuthentication can be subtle
Set custom IIdentity or IPrincipal / Store user id in Principal or Identity?

Could you not switch the two around and store the member id in the form variable (since I assume the user is able to change there email address and not there member id)...
Dim memberId as Integer = GetMemberIDByEmail(VisitorEmail)
' assuming integer here and that a result is found etc etc
' set the form authentication stuff
FormsAuthentication.RedirectFromLoginPage(memberId, False)
And then you can always look up the email address from the memberId (caching it perhaps against the member id across requests)
Public Function GetMemberEmail(Byval memberId as Integer) As String
Dim cacheKey as String = "member-email-" & memberId
Dim email as String
If Cache.Item(cacheKey) is Nothing Then
email = GetMemberEmailByID(memberId)
Cache.Insert(cacheKey, email ...
Else
email = Cache.Item(cacheKey)
End If
return email
End Function
If you need both pieces of information, and the Id is less likely to change, it would seem the better value to be used for your forms authentication....and you can always look up the email address from the value.

Related

Is it alright to put sensitive data into the cache? ASP MVC

I currently implemented some "checkers" for my MVC application.
So far here is what i have,
Authorization(Form)
Authentication (Custom RoleProvider)
Action Filters(to make sure that the user won't put any bogus id numbers or try accessing the other's data by editing the GET url.
I have several questions regarding the best practices for cache on ASP MVC.
Here is my implementation of my login:
[HttpGet]
[ActionName("login")]
public ActionResult login_load()
{
return View();
}
[HttpPost]
[ActionName("login")]
public ActionResult login_post(string uname,string pword)
{
using (EmployeeContext emp = new EmployeeContext())
{
//h student log = new student();
int success = emp.login.Where(x => x.username == uname && x.password == pword).Count();
if (success == 1)
{
int id = (from logs in emp.login
join rol in emp.roles on logs.role equals rol.id
where logs.username == uname
select logs.id).First();
FormsAuthentication.SetAuthCookie(uname, false);
HttpRuntime.Cache.Insert("id", id);
return RedirectToAction("Details", "Enrollment", new { id = id});
}
return View();
}
}
(I plan on implementing H&S as soon)
Anyway, here are my concerns so far:
For security concerns, would it be fine to store something like id's on cache? or it's better if i use sessions ?
Let's say i successfully logged in, and i add another line of this code :
HttpRuntime.Cache.Insert("id", id);
Is it going to edit my previous record or it's going to add another entry?
I have this code from my Custom RoleProvider, HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration); and i believe that they are "fired" everytime i ask a controller with a protection of [Authorize(Role="users")]. So does it make a new entry or it edits the previous/existing one?
Should i worry about deleting/clearing my cache as soon as the user decided to log out? my role provider timeout is currently set to 20 minutes
I need the id because aside from the username, it is my unique identifier and i use it to compare on whatever id the user is trying to access.
I am thinking if it would be possible to edit the cache and use it against my application.
Don't worry about storing the ID, you need to go back and refactor to use the inbuilt identity stuff that's in the box for MVC. Looking at your code I can only assume that this system would store passwords in plain text. You will not pass any compliance with a system like this.
Rule of thumb when it comes to "is this secure" is don't write it yourself. Find a proven product and use that.
If for whatever reason the inbuilt identity system that is provided with MVC doesn't work for your requirements, have a look into this: https://github.com/brockallen/BrockAllen.MembershipReboot
FYI:
Identity system is the service that logs people in, out and manages the logged in user. Feel free to head over to this to learn more about the inbuilt system for MVC: http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity
For security concerns, would it be fine to store something like id's on cache? or it's better if i use sessions ?
In this case it doesn't make a lot of difference. However, the cache cannot be distributed across multiple web servers. Session state can by changing the <sessionState> section of the web.config file. So you are building an inherent limitation in scalability into your application by using cache.
Let's say i successfully logged in, and i add another line of this code :
HttpRuntime.Cache.Insert("id", id);
Is it going to edit my previous record or it's going to add another entry? I have this code from my Custom RoleProvider, HttpRuntime.Cache.Insert(cacheKey, roles, null, DateTime.Now.AddMinutes(_cacheTimeoutInMinute), Cache.NoSlidingExpiration); and i believe that they are "fired" everytime i ask a controller with a protection of [Authorize(Role="users")]. So does it make a new entry or it edits the previous/existing one?
First of all, you have a major flaw in your code. Cache is intended to be shared between all users on the site. So, when you insert a value, such as HttpRuntime.Cache.Insert("id", id);, all of the users will see it. If you are using this value to lookup data later, then the user data will always be for the last user that signed in.
You can fix this by adding adding a value unique to the user to the key.
var key = this.User.Identity.Name + "|Id";
HttpRuntime.Cache.Insert(key, id);
Note I am using a pipe character as a separator here. This is assuming the user name doesn't allow a pipe character (which you would also need to ensure).
Secondly, using a proper cache pattern means that you will never need to worry about whether "id" exists because you have already made that check. Using the cache typically looks like this.
public static string GetUserID()
{
// Check whether the user is logged in
if (!HttpContext.Current.User.Identity.IsAuthenticated) {
return 0;
}
// Make a UNIQUE key that can be used for this scenario
var userName = HttpContext.Current.User.Identity.Name;
var key = userName + "|Id";
// Attempt to get the ID from the cache
var id = HttpRuntime.Cache[key];
// A null value indicates there was no value in the cache
if (id == null)
{
// No ID in the cache, look it up from the database
using (EmployeeContext emp = new EmployeeContext())
{
id = (from user in emp.login
where user.username = userName
select user.id).First();
}
// Store the ID from the database into the cache
HttpRuntime.Cache.Insert(key, id,
// No Dependencies
null,
// No absolute expiration (mimic the behavior of forms authentication)
System.Web.Caching.Cache.NoAbsoluteExpiration,
// Timeout 20 minutes after the last access
// (to mimic the behavior of forms authentication)
new TimeSpan(0, 20, 0),
// Setting to NotRemovable ensures that if the
// application pool restarts, you don't lose your cache
System.Web.Caching.CacheItemPriority.NotRemovable,
// No callback needed here
null);
}
return (string)id
}
Of course, you can improve performance if the value is available at login by inserting it into the cache directly, but you need to ensure you use the same key in that case.
Session is probably a better choice in this scenario, but either way you should make use of this pattern to double check you have a value before returning it to the user.
Should i worry about deleting/clearing my cache as soon as the user decided to log out? my role provider timeout is currently set to 20 minutes
If you use Session state instead of cache this is much easier. Simply call Session.Abandon() when the user logs out.

Is it safe to use a Shared property for authentication verification?

Should I be using Shared, or just a Public function and initiate a class when needed? I have read a number of articles, but still cant get my head around the best option here.
Here is the code I want to get. The userid in question is a string that's set when the user logs in. As I may need the userid in a number of pages, I want to add it in a class.
Public Shared ReadOnly Property userid As String
Get
Dim ck As HttpCookie = HttpContext.Current.Request.Cookies(FormsAuthentication.FormsCookieName)
Dim tkt As FormsAuthenticationTicket = FormsAuthentication.Decrypt(ck.Value)
Return tkt.Name
End Get
End Property
I would almost never say that you should leverage a Shared member when dealing with authentication - but since you're using Cookies I can't see any reason this code wouldn't work as is and make it easier to access.
It is perfectly fine and acceptable. You can see accessing HttpContext.Current inside static method in a lot of open source projects.
FYI: HttpContext.Current must be on current calling thread, so make sure to check HttpContext.Current is not null.

ASP.NET session key for web service security?

I've got a web service (ASP.NET 2.0) that I'm calling from javascript using jQuery's $.ajax(). I've been told that the session key is often used as a nonce in a situation like this for security purposes; the web service takes a key as one of its parameters and only returns data if it matches the current session key.
I'm attempting to accomplish this by setting the value of a hidden field to the current SessionID on every Page_Load (i.e. every postback), then grabbing it in the javascript to pass as a parameter. But it's never the same key as the web service's current key (Context.Session.SessionID).
Is this possible to resolve, or should I be doing this another way?
EDIT: code to set session in hidden field as requested.
hfSession.Value = Context.Session.SessionID;
That's in the Page_Load of a .ascx control, not under any conditional (i.e. not wrapped with if (!Page.IsPostBack).
I believe you are trying to prevent Cross Site Script Request Forgery (CSRF). The Session ID is actually sent across as a cookie and the attacker can set this. Rather than use the Session ID itself, you should use a randomly generated number stored in a Session variable.
String GetCSRFToken()
{
String token = (String)Session["CSRFToken"];
if( token == null )
{
token = GenerateLongRandomString();
Session["CSRFToken"] = token;
}
return token;
}
void AssertValidCSRFToken(String token)
{
if( token != GetCSRFToken() )
{
throw new Exception("Invalid Request!")
}
}
Here is a link to another question with more info on preventing this kind of attack:
CSRF Validation Token: session id safe?
Asp.net actually generates a new Session ID for every request until you use the Session State to store some value. This could be a reason why the values are different. Try and save something in the session. Perhaps
Session["SessionID"] = Context.Session.SessionID;
hfSession.Value = Context.Session.SessionID;
A new SessionID is generated each time the page loads until the session is actually allocated. So if you don't actually allocate anything to the session, the SessionID will change upon each page load.

How to protect cookies from an attack

I want to use cookies for storing userId during the session which lets to avoid unnecessary roundtrips to the data base. This userId is used to access some user specific information. As cookies can be easily edited I'm now conserned with the security issue.
In order to forbid an logged in user to edit their userId and so get access to other users' information I use a pretty straightforward method. I add one more cookie at the userId cookie creation moment which stores a hashed value for it. While hashing I use a hard coded 64 byte key. When retrieving the userId from the cookie it is always checked if it matches with its hashed value.
Here is basically my code:
public static int GetUserId(Page page)
{
int userId;
if (page.Request.Cookies["userId"] != null && page.Request.Cookies["userIdHashed"] != null)
{
string userIdHashed = page.Request.Cookies["userIdHashed"].Value;
string userIdCoockie = page.Request.Cookies["userId"].Value;
string coockie = (userIdCoockie + "945AFF2FD0F1D89B4B1DBEB1B0C5D3B8B5DCE000AAEA331EB0C3F3A68C3865EFA73BC6EBF30C8DF1AD6B9ECB7094DA5B0C1AF36B5BBD096E3D873E9589E3F664").GetHashCode().ToString();
if (userIdHashed == coockie)
{
userId = Int32.Parse(userIdCoockie);
}
else
{
throw new Exception("UserId does not match!");
}
}
else
{
userId = ...//here userId is being retrieved from the data base and than:
page.Response.Cookies["userId"].Value = userId.ToString();
page.Response.Cookies["userId"].HttpOnly = true;
string userIdHashed = (userId.ToString() + "945AFF2FD0F1D89B4B1DBEB1B0C5D3B8B5DCE000AAEA331EB0C3F3A68C3865EFA73BC6EBF30C8DF1AD6B9ECB7094DA5B0C1AF36B5BBD096E3D873E9589E3F664").GetHashCode().ToString();
page.Response.Cookies["userIdHashed"].Value = userIdHashed;
page.Response.Cookies["userIdHashed"].HttpOnly = true;
}
return userId;
}
So my questions are:
Can such an approach be considered
reliable enough in this situation?
If not should I modify it and how or
should I look for something different
(e.g. encryption/decryption via
System.Security.Cryptography as
recommended here)?
And additional question: Does it really make sense to set HttpCookie.HttpOnly = true to prevent javascript from accessing the cookie given that it can also easily be modified by the user?
UPDATE
Great thanks for answers to Kerrek SB and Darin Dimitrov who share the opinion that it does not make sense to try to protect cookies on my own taking into account that there are already built in protected mechanisms of storing of such kind of information between postbacks.
Options suggested are:
Using the ASP.NET cache (but I believe it is generally supposed to
store information which should be shared between users, so I look at
other two options).
Adding a custom string with userId into UserData part of the
FormsAuthenticationTicket.
Using the Session State.
So currently I'm deciding between the latter two.
Changing the FormsAuthenticationTicket is not really straightforward. Additionally it does not work with the Cookieless Forms Authentication (as stated here).
Using the Session State is much easier but it can affect the performance because it stores the values in the server memory. However may be in my case it is not so dramatic because we store only userId of type int.
So for now the last option looks much better for me. However I would greatly appreciate if anybody else could comment and support or criticise any of options discussed.
Thanks in advance!
You seem to be reinventing some wheels here. While this could be acceptable when doing some standard code, when security is involved this almost always leads to catastrophic consequences.
To track actively logged in user I would recommend you using forms authentication (and here's another useful tutorial). It uses authentication cookies to track users. Those cookies are securely encrypted by the framework so that they cannot be modified using the <machineKey> section of the server machine.config file.
Form your code all you need to do to access the currently logged in user name is the following:
public static string GetUserId(HttpContextBase context)
{
if (context == null || !context.User.Identity.IsAuthenticated)
{
return null;
}
return context.User.Identity.Name;
}
You really shouldn't be handling all this stuff manually especially when the ASP.NET framework has a built-in mechanism for it.
That's terribly roundabout and obscure. If you already have an active session, why don't you just store this kind of data (which the client never needs to know, mind you) in your server-side session data?
All you should ever need to exchange with the client is the session ID, really.
you can also encrypt the cookies with Secure Socket Layer
HttpCookie cookie = new HttpCookie();
cookie.Secure = true;

Asp.net, where to store the username of logged in user?

When a user log into my asp.net site I use the following code:
FormsAuthentication.RedirectFromLoginPage(userid, false);
As I often need to use the userid I can then later get the userid by:
string userid = System.Web.HttpContext.Current.User.Identity.Name;
Now I also want to show the logged in username on each page and my questions is therefore where do I place the username best if I need to use it on every page. User.Identity.Name is already taken by the userid so I can't use that one. Another solution would be to get the username from the database on each page, but that seems like a bad solution.
So: Is the best way to use Sessions to store the username?
There are essentially 6 different ways to store information, each with it's own benefits and drawbacks.
Class member variables. These are only good for the life of one page refresh.
HttpContext variables. Like class member variables, only good for one page refresh.
ViewState, these are passed from page to page to keep state, but increase the size of the downloaded data. Also, not good for sensitive information as it can be decoded.
Cookies. Sent on each page request. Also not good for sensitive information, even encrypted.
Session. Not passed to the end user, so good for sensitive information, but it increases the resource usage of the page, so minimizing usage for busy sites is important.
Authentication Cookie User Data - This is like like cookies, but can be decoded with the authentication data and used to create a custom IIdentity provider that implements your desired Identity information, such as Name or other profile information. The size is limited, however.
You can store just about anything in SessionState in asp.net. Just be careful and store the right things in the right places (you can also use ViewState to store variables.
Check this out for how to use SessionState to store and retrieve variables across postbacks.
public string currentUser
{
get { return Session["currentUser"] as string; }
private set { Session["currentUser"] = value; }
}
Using sessions isn't a bad idea but make sure to check for NULL when retrieving the values for when the sessions time out.
Or you could pass the variable through in the URL e.g
/Set
Response.Redirect("Webform2.aspx?Username=" + this.txtUsername.Text);
/Read
this.txtBox1.Text = Request.QueryString["Username"];

Resources