I was thinking yesterday how to solve this issue, because everything what i give or check about user is depended of his ProviderUserKey (ID).
So i made one static function like
public static Guid GetUserID()
{
string UserID = string.Empty;
if(HttpContext.Current.Session["UserID"] != null)
{
UserID = HttpContext.Current.Session["UserID"].ToString();
}
if(!string.IsNullOrEmpty(UserID))
{
return new Guid(UserID);
}
UserID = Membership.GetUser().ProviderUserKey.ToString();
HttpContext.Current.Session["UserID"] = UserID;
return new Guid(UserID);
}
Main point of this class is to reduce database connections to check/get user ID.
My problem with this function is not that this is not working, my problem is what if logged user log out and log with another account?
Or Is it better to add session value on log in and clear session value on log out?
Where you can see any other problem with this kind of "Get User ID"?
If you log the user out then you should also be killing the session.
When you login as another user you would also have the session reinitialized.
Note you'll want to keep the session and forms auth timeouts (assuming you are using forms auth) in sync with each other:
How can I handle forms authentication timeout exceptions in ASP.NET?
This should help keep the session in line with the forms auth token. You'll in turn need to kill the session on logout and intialize it upon login.
Another alternative is to implememt your own membership provider that caches this key to prevent constant db hits.
have you tried using the ProfileProvider?
you can use and customize with special properties and that's is managed by session each user.
example to get values:
HttpContext.Profile.GetPropertyValue["CustomProperty"]
In this video you can lean to implement it, create, configure and use...
http://www.asp.net/web-forms/videos/how-do-i/how-do-i-create-a-custom-profile-provider
Related
Based on what i have understood we have
sessionId is stored in the cookie .AspNetCore.Session
Deleting the cookies and Clearing the session does nothing.
context.HttpContext.Session.Clear();
foreach (var cookie in context.HttpContext.Request.Cookies.Keys)
{
context.HttpContext.Response.Cookies.Delete(cookie);
}
So the question is can we change the sessionId somehow, or is there a way to protect us from Session-Fixing?
...or is there a way to protect us from Session-Fixing?
Yes there is! OWASP states:
Unfortunately, some platforms, notably Microsoft ASP, do not generate new values for sessionid cookies, but rather just associate the existing value with a new session. This guarantees that almost all ASP apps will be vulnerable to session fixation, unless they have taken specific measures to protect against it.
The same page recommends an approach for ASP.Net, which we used for all of our ASP.Net applications and which passed pen testing. I think it is still valid for ASP.Net Core:
The idea is that, since ASP prohibits write access to the ASPSESSIONIDxxxxx cookie, and will not allow us to change it in any way, we have to use an additional cookie that we do have control over to detect any tampering. So, we set a cookie in the user’s browser to a random value, and set a session variable to the same value. If the session variable and the cookie value ever don’t match, then we have a potential fixation attack, and should invalidate the session, and force the user to log on again.
This is a simplified example of how we approached this in .Net Core Razor Pages and should give you an idea of how to implement it yourself:
public IActionResult OnPost()
{
Login();
return Redirect("~/Login");
}
private void Login()
{
// Check the user's credentials and do all the other necessary stuff.
// ...
// Create the random value we will use to secure the session.
string authId = GenerateAuthId();
// Store the value in both our Session and a Cookie.
HttpContext.Session.SetString("AuthId", authId);
CookieOptions options = new CookieOptions()
{
Path = "/",
HttpOnly = true,
Secure = true,
SameSite = Strict
};
Response.Cookies.Append("AuthCookie", authId, options);
}
private string GenerateAuthId()
{
using(RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] tokenData = new byte[32];
rng.GetBytes(tokenData);
return Convert.ToBase64String(tokenData);
}
}
Check the content of the Session and Cookie wherever you need it. If they don't match, you should Clear the Session (I don't think Session.Abandon is still available in .Net Core) and log out the user.
public void OnGet()
{
string cookieValue = Request.Cookies["AuthCookie"];
string sessionValue = HttpContext.Session.GetString("AuthId");
if (cookieValue == null || sessionValue == null || cookieValue != sessionValue )
{
// Invalidate the session and log out the current user.
}
}
Session.Clear only removes all data from the session, it does not actually remove the session itself. That will occur when the timeout is hit. It was an odd choice, in my opinion, for the ASP.NET Core team to not have implemented Session.Abandon, as previously existed, since that actually would remove the actual session, itself.
As long as the actual session still exists, even if the data for it no longer does, it can still be retrieved by that session id, as a result, the problem shifts to the client-side.
Importantly, the server cannot actually make the client do anything. Calling Cookies.Delete really only sends a new Set-Cookie response header for the same cookie with an expiration date in the past. This should prompt the client (browser, most likely) to then remove that cookie, since it is now expired. However, that is totally 100% on the client, so if there's a bug or the client otherwise is not picking up the change or the client simply refuses to comply for whatever reason, the cookie will remain. Then, again, if the cookie still exists and the session identified by the session id it contains still exists, it can be restored.
Long and short, the code you have should work, and there's really not anything else you can do other than what you're already doing. If the session isn't being abandoned, there's some other issue somewhere (most likely with the client).
I have set sessions for my properties/methods in WebUser like following
public function getRole(){
$user = $this->loadUser(Yii::app()->user->id);
$this->setState('roleId', $user->roles_id);
return $user->roles_id;
}
In the config, I have set autostart sessions to 'true', cookieMode to 'only'. I understand that when i 'setState', the cookie with the same name is also created along with the session variable. Currently I am calling these variables using Yii::app()->user->roleId;
My question is this:
a) To utilize from the cookies and/or the session variables already set, should i call them using Yii::app()->request->cookies['roleId']; or Yii::app()->session['roleId']?
b) Will calling Yii::app()->user->roleId get me the value if it is already set as Cookie or Session instead of running the whole method again?
I appreciate your support!
I would only use sessions for that. Cookies can be easily tampered with and a user could probably assign himself another role id. Session is somewhat safer.
Use your WebUsers getRole() for that, which caches it in the session like you have above. This is fine. Just put a check at the top, if there is a session var roleId instantly return it. If not, load the user and get it there like you already have.
I'm working on open-source application "Project-Open" and during the scanning I got the following vulnerability:
[Medium] Session Identifier Not Updated
Issue: 13800882
Severity: Medium
URL: https://<server_name>/register/
Risk(s): It is possible to steal or manipulate customer session and cookies, which might be used to impersonate a legitimate user,allowing the hacker to view or alter user records, and to perform transactions as that user
Fix: Do not accept externally created session identifiers
though the fix is mentioned but it is not sufficient for me to understand it completely.please guide me how should I remove this.Also let me know if any further details are needed to understand the question.
The project source code is in tcl
I found the following code which does the same but it's in java.
public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException {
// get the current session
HttpSession oldSession = request.getSession();
// make a copy of the session content
Map<String,Object> temp = new ConcurrentHashMap<String,Object>();
Enumeration e = oldSession.getAttributeNames();
while (e != null && e.hasMoreElements()) {
String name = (String) e.nextElement();
Object value = oldSession.getAttribute(name);
temp.put(name, value);
}
// kill the old session and create a new one
oldSession.invalidate();
HttpSession newSession = request.getSession();
User user = ESAPI.authenticator().getCurrentUser();
user.addSession( newSession );
user.removeSession( oldSession );
// copy back the session content
for (Map.Entry<String, Object> stringObjectEntry : temp.entrySet()){
newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue());
}
return newSession;
}
P.S. I'm newbie in TCL.
please let me know if you need any further explanation.
There is a fix in OpenACS 5.9 that addresses your scanning reports. Please see the following discussion on OpenACS.org for reference.
http://www.openacs.org/forums/message-view?message_id=5332821
The problem that the OWASP report is talking about is the inability to migrate a session to use a new ID, making it easier for an attacker to discover the ID and reuse it. The protection against this is to change the session ID from time to time (no, I don't know how often!) and that Java code is involved in doing just that.
A session is represented as a token stored in the browser, usually in a cookie (and this is what cookies are designed to do). That token is then used to look up the database record corresponding to the session, which holds serializations of the key/value mappings in the session. It's a simple mechanism, but very powerful. The Java code for doing all this will be fairly complex behind the scenes because of the serialization, etc., but Tcl values are (usually, and always for built-in types) naturally serializable and so should prove much less of a problem in this; copying a session to a new key could be done without having to deserialize in the first place.
The exact code for doing this depends on the framework in use. I don't know what ]project-open[ uses, so that's as far as we can drill right now. You need to talk to other people actually working on PO…
For all that, the best way would be to make the key given to clients not be the primary key, so that you can change the session key without having to delete things. Just have a session key column (with an index!) and you'll be able to make things work fine. This is a more sophisticated approach though; it might not be practical to implement in your environment..
I have an MVC 5 application that I lock down by only allowing certain authenticated users to have access to specific actions within my controller. I utilize the authorize attribute at the top of the class allowing only the user(s) I want to gain access after login. I do this with the following attribute placed at top of my class...
[Authorize(Users="user1,user2")]
This works great! However, what if I don't want to recompile and deploy the application everytime I want to add a new user to this specific controller?
I thought I might add this in my web.config file under as a key like so...
<appSettings>
<add users="user1,user2"/>
</appSettings>
But when I try to access this key in my controller like so: [Authorize(Users=ConfigurationManager.AppSettings["users"])] I am getting an error: Cannot resolve symbol 'AppSettings'.
Is there a way to do this?
I'm not sure why an answer that didn't answer the question was accepted. Regardless, I thought it might be worth adding an answer for any future travelers.
While this functionality isn't provided out of the box, it's certainly possible by writing your own authorize attribute.
public class ConfigAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var allowedUsers = ConfigurationManager.AppSettings["CoolActionAllowedUsers"];
var allowedUsersArray = allowedUsers.Split(',');
if (httpContext.User.Identity != null && allowedUsersArray.Contains(httpContext.User.Identity.Name))
{
return true;
}
return false;
}
}
And to use the attribute:
[ConfigAuthorize]
public ActionResult CoolAction() {
//...
}
In the code above when your authorization is performed in AuthorizeCore, the configuration value from CoolActionAllowedUsers will be pulled into memory and the currently authenticated user will be verified if they are in the list of allowed users. If you make a change to your config file it won't be a problem; the application pool will automatically restart and the next time the code runs to read the config file your new value will be read.
I completely agree with #Shoe that roles should be used. Managing a list of users in your code is just a pain in the arse. In fact, at work, anytime I get a request for just one random user to have access to a page I always require a group to be setup. However the code above could apply to a list of roles as well.
Instead of using the Users parameter use the Roles parameter.
[Authorize(Roles="CanExecuteActions")]
Now you can manage what users have access to your controller by giving them this role. Any user without the role can't execute any actions of the controller.
I have an application where one username can belong to many companies. Thus to distinguish them, i need to use the both username and password as unique pair to login.
I'm using ASP.NET MVC and i struggle to understand where the Login occurs.
Actually i can see where it validates the user but i don't find where it retrieves the user.
So where the
Select user where username=xx and password=xx occurs ?
Asked differently : i did not find wher User is set ? I see User.Identity.Name it in the code, but i don't see :
User=Select....
Thanks
John
John, as you are using MVC. You wont be seeing any queries in the code aside from the LINQ syntax. Im guessing what you are trying to do is a many to many relationship between the User table and the Company table. (one user has multiple companys and 1 company has multiple users)
Pretty much database wise this would mean you need an extra table with both primairy keys of Company and Users.
To get back to your question. ASP.net MVC has its own membership provider. You can choose to either use the default one with its own tables or overwrite it and create ur own custom membership provider (with the ability to use ur own user table)
The default one pretty much should contain most of the basic attributes. (password reset, password salt, email,..)
http://www.asp.net/web-forms/tutorials/security/membership/creating-the-membership-schema-in-sql-server-cs
skip to the step: Installing the Application Services to generate the tables
However guessing you already have a database with your very own user table. you should overwrite the custom membership class.
Simply this would mean you make a new class that inherits from the abstract class "MembershipProvider"
public class MyMembershipProvider : MembershipProvider
{
}
After that you have to let asp know that you will be overwriting the default membershipprovider with yours in web.config:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
applicationName="MyApp"
Description="My Membership Provider"
passwordFormat="Clear"
connectionStringName="MyMembershipConnection"
type="MyApp.MyMembershipProvider" />
</providers>
</membership>
Some methods in the membership provider requires you to return or use an object of MembershipUser. Everything of how to implement this is right here:
http://msdn.microsoft.com/en-us/library/system.web.security.membershipuser.aspx
This is not a necessary step but its recommended.
Good luck john :)
If theres any confusion in the explanation, dont hesitate to ask
Short Answer:
The code you're describing happens behind the scenes in the LogOn action of the Account Controller:
MembershipService.ValidateUser(model.UserName, model.Password)
Which returns true for a valid user. The user is then "signed in" with the next line in the code:
FormsService.SignIn(model.UserName, model.RememberMe)
(You can see both of those functions defined in the AccountModels file under the Models folder)
If you want to also check company id while authenticating the user then you'll need to write your own auth method to replace ValidateUser. Ths will depend on what you're using for your store (SQL?)
But, as a broader point, best practices you should not allow the same user name for different users. It's just a bad idea and will lead to trouble.
UPDATE:
If I were recommending how to do this, I would suggest you user the UserProfile aspect of ASP.NET Membership. It is designed to capture and store additional user variables (such as company) while still using the nicely built and secure Membership that they've written for you. Read up on it, but below is my CreateUser function in the app I'm currently working on. Note how I use the Profile to store first and last name as well as a flag that the user needs their password reset.
Again, this would preempt the ability to have multiple users with the same username, but I really think you ought to avoid that.
[HttpPost]
public ActionResult CreateUser(string username, string email, string first, string last, string role)
{
string password;
MembershipUser user;
//Generate a random password
password = Auth.CreateRandomPassword(6);
try
{
//Create the user
user = Membership.CreateUser(username, password, email);
//Add the user to the chosen role
Roles.AddUserToRole(username, role);
//Create the user profile
UserProfile profile = UserProfile.GetUserProfile(username);
profile.FirstName = first;
profile.LastName = last;
profile.ForcePasswordReset = true;
profile.Save();
EmailNewUser(username, email, password);
}
catch (Exception ex)
{
HttpContext.Response.StatusCode = 500;
HttpContext.Response.StatusDescription = ex.Message;
HttpContext.Response.Clear();
}
return PartialView("UserTable", Auth.Users());
}