Prevent Last 'N' password reuse - MVC 3 - asp.net-mvc-3

I have a login form that I created in MVC 3 which has a 'change password' view.
At the moment there are no restrictions with respect to reusing previous passwords.
I understand that I'd need to create a custom password manager as ASP.net has nothing out the box for this.
I have a 'previousPassswords' table created.
currently , my 'changePassword' controller looks like this:
if (ModelState.IsValid)
{
// ChangePassword will throw an exception rather
// than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
changePasswordSucceeded = currentUser.ChangePassword(model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
I'd like to compare new passwords (preferrably encrypted) with the last 'n' passwords in the previousPasswords table an act accordingly.
Is this the recommended approach or is there a better way ?

You shouldn't check on previous passwords (compare: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-63b.pdf).
First, it increases the Chance that users go for easier and therefore more insecure Passwords:
'I can't use myPassword as Password any longer? well the make it password then'. Or worse: 'I can't use my individual Password? Well, I'm gonna use the email password then'
Secondly, if your user data get stolen/hacked the effect will be even worse: you won't loose one Password per user but multiple.
For Password policies, check Paragraph 5.1

Related

Migrating User to Cognito on Sign In

I am trying to migrate users to Cognito when they sign in the first time. For this I wrote a lambda function that does call an API to check if the users exist in db or not ? if the user exists, it will be created in cognito but I am not sure how do I tell the application that user is created and it should allow the user to login .
Here is the code in c#:
public async Task<Stream> FunctionHandlerAsync(Stream stream, ILambdaContext context)
{
RootObject rootObj = DeserializeStream(stream);
User user = new User(rootObj.userName, rootObj.request.password);
ApiResponse apiResponse = await MobileAuthenticateAsync(user.UserName, user.Password);
// Considering apiResponse returns "user authenticated", we create the user in //cognito. This is working.
// How do I send response back to Application so it knows that user is // //created and authenticated and should be allowed to login.
//Before returning stream, I am setting following 2 status.
rootObj.response.finalUserStatus = "CONFIRMED"; // is this correct ?
rootObj.response.messageAction = "SUPPRESS";
return SerializeToStream(rootObj);;
}
You're pretty close.
You can see the full documentation on the Migrate User Lambda Trigger page, however in short you need your response to look like:
{
response: {
userAttributes: {
email: 'user#example.com',
email_verified: true,
custom:myAttribute: 123,
},
finalUserStatus: 'CONFIRMED',
messageAction: 'SUPPRESS',
forceAliasCreation: false,
}
}
Where:
userAttribute: this is a dictionary/map of the user's attributes keys in cognito (note that any custom attributes need to be prefixed with custom:), to the values from the system you're migrating from. You do not need to provide all of these, although if you're using an email alias you may want to set email_verified: true to prevent the user having to re-verify their e-mail address.
finalUserStatus: if you set this to CONFIRMED then the user will not have to re-confirm their email address/phone number, which is probably a sensible default. If you are concerned that the password is given as plain-text to cognito this first-time, you can instead use RESET_REQUIRED to force them to change their password on first sign-in.
messageAction: should probably be SUPPRESS unless you want to send them a welcome email on migration.
forceAliasCreation: is important only if you're using email aliases, as it stops users who manage to sign-up into cognito being replaced on migration.
If you respond with this (keeping the rest of the original rootObj is convenient but not required then the user will migrated with attributes as specified.
If you throw (or fail to respond with the correct event shape) then the migration lambda fails and the user is told that they couldn't migrated. For example, because they do not exist in your old user database, or they haven't provided the right credentials.

Optemizing basic auth: Save user authentification for follow up requests

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.

Symfony2 shared users across multiple apps

I have multiple symfony2 applications which share common entities, but use different database settings. Each of these databases has tables user, user_role and role.
Here's the catch: I would like that user to be able to login to app1 by visiting www.myproject.com/app1/login and after changing URL to /app2/ to use existing token ONLY if identical user exists in app2's database (same username, password and salt). Currently it checks only for same username which is, you must agree, quite inconvenient...
I can't really see when refreshUser() is being called... :-/
All apps use same User and Role entities and UserRepository.
Any help would be much appreciated!
UserRepository:
class UserRepository extends EntityRepository implements \Symfony\Component\Security\Core\User\UserProviderInterface{
/** #var User */
private $user;
public function loadUserByUsername($username) {
/** #var $Q \Doctrine\ORM\Query */
$Q = $this->getEntityManager()
->createQuery('SELECT u FROM CommonsBundle:User u WHERE u.username = :username')
->setParameters(array(
'username' => $username
));
$user = $Q->getOneOrNullResult();
if ( $user == null ){
throw new UsernameNotFoundException("");
}
return $this->user = $user;
}
public function refreshUser(UserInterface $user) {
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class) {
return $class === 'CommonsBundle\Entity\User';
}
public function findById($id){
return $this->getEntityManager()
->createQuery('SELECT u FROM CommonsBundle:User u WHERE u.id = :id')
->setParameters(array(
'id' => $id
))
->getOneOrNullResult();
}
}
User#equals(UserInterface):
I know there is a prettier way to write this method but I will rewrite it after see this working :)
public function equals(UserInterface $user)
{
if (!$user instanceof User) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->getSalt() !== $user->getSalt()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
Your question made me think. When using symfony2 security, you got one problem: Either a session is valid, meaning the user is authenticated as either anonymous or real user, or the session is invalid.
So, with this in mind, I don't see your approach working as you would like it, because let's say user1 logs in and is using app1. Now he switches to app2 and is not in the database, meaning he should not have access. What to do now? Invalidate the session? This would mean he has to log in again in app1.
If you would use subdomains, you could tie your session to that subdomain, but this would mean the user has to log in again for each application.
There is another problem: It seems like symfony2 stores the id of the user into the session, so without access to the app1 database, you cannot know what the password and the roles of the user in the app1 database are and cannot check for it.
I guess the security of symfony2 was simply not made for such behaviour. It expects the session to relate to the same user within your whole application.
I don't think that symfony2 is the big problem here but the overall handling with php. Let's think for one moment what I would suggest without symfony2:
When a user logs in, store user and roles into a specific array in the session, like:
user.app1 = array('username','password',array('role1','role2'))
Now, on each request to app1 I would check if user.app1 is in the session and read the roles from there. If not, I would check for user.app2, user.app3 and so on. If I find none, redirect to login. If I find one, I would query the database to find the user with the same username and compare the other values. If match, store everything into the database. If not, check next user from session.
I looked up the symfony security reference, and you got some extension points, so maybe you can work from there on. The form_login got a success_handler, so adding the array to the session as suggested above should be done there. The firewall itself has some parameters like request_matcher and entry_point which could be used to add additional checks like the ones I mentioned above. All are defined as services, so injecting the entity manager and the security context should be no problem.
I personally think the design itself is not optimal here and you might be better of refactoring your code to either use one user for all apps and different roles (remember that you can define many entity managers and use different databases) or even consolidating all databases and storing everything into one database, using acl to prevent users from viewing the "wrong" content.

MVC 3 FormsAuthentication and disabled user accounts

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;
}
}

Crypto.HashPassword and VerifyHashedPassword

As titled, I'm using MVC 3 helper Crypto to hash my password, then i save nothing except encrypted password into database.
When I implement the login form, I make a boolean function, retrieve password stored in database
and get the boolean result with this helper:
Crypto.VerifyHashedPassword(password in DB, password in login form)
if true, then login
if false, the display "password wrong"
This few day, I was trying to test my login function. I found one fact: If a new registrar comes to register at the system, the previous user will be failed to login, displaying "password wrong".
Is there any problem with my program or did I miss something?
Function to return bool
if (Crypto.VerifyHashedPassword(parent.Password, password))
{
return true;
}
else
{
ModelState.AddModelError("", "You was input password wrongly!");
return false;
}
Code to hash and store the password
parent.Password = Crypto.HashPassword(parent.Password);
peoplemanager.Add(parent);
I would advise you to follow this Tutorial I found it very helpful when creating my membership solution:
http://theintegrity.co.uk/2010/12/asp-net-mvc-2-custom-membership-provider-tutorial-part-3/

Resources