Determine AD password policy programmatically - c#

I have been using the System.DirectoryService (ADSI) classes and methods to create and change users in an Active Directory.
Recently we added a feature to allow users to set their own password through the system. However, using the SetPassword method throws an exception when the password is not accepted by the Password Policy set.
userEntry.Invoke("SetPassword", new object[] {password});
My question is: How do I check to see if a password lives up to the password policy, before attempting to use the SetPassword-method?
I read in this post that you can get the Password Policy-settings from the root domain node, but where can I read more about what each attribute means? For instance, which characters are required to fullfill the "Complexity" policy?
Once I know this, I can implement my own password check-method, but as this is an error-prone approach, I would rather use a built-in check and give the user appropriate info on what is wrong with their password.

I am working on a similar project at my work. We are rolling a forgot password application. I ended up just doing an Invoke("SetPassword", "[randomString]") and saved the random string for the Invoke("ChangePassword","[randomString]","[user supplied pw]"). The result of the ChangePassword was returned to the user.
SetPassword does not check for password complexity or history rules. It is the same as right clicking a user in AD and selecting "Reset Password." ChangePassword however, does check for password history requirements.

The API function you want is NetValidatePasswordPolicy.
There are three modes it operates in:
NetValidateAuthentication: if you are authenticating a user; so the function can check password expiration policies, bad login attempts, account lockouts, bad login attempts, etc
NetValidatePasswordChange: if the user is changing their password; so the function can check against lockout, or against the password policy
and the mode you want:
NetValidatePasswordReset: you are an admin resetting a user's password; which only checks the password complexity.
I'll try transcoding from another language to C# on the fly; but you will have to P/Invoke it.
<summary>Check password during password reset.
The result from NetValidatePasswordReset, this member can be one of the following values.
NERR_Success The password passes the validation check.
NERR_PasswordTooShort Validation failed. The password does not meet policy requirements because it is too short.
NERR_PasswordTooLong Validation failed. The password does not meet policy requirements because it is too long.
NERR_PasswordNotComplexEnough Validation failed. The password does not meet policy requirements because it is not complex enough.
NERR_PasswordFilterError Validation failed. The password does not meet the requirements of the password filter DLL.
</summary>
UInt32 TestPasswordComplexity(String username, SecureString password)
{
//All code on stack overflow is in the public domain; no attribution
//is required.
const UInt32 NetValidatePasswordReset = 3;
NET_VALIDATE_PASSWORD_RESET_INPUT_ARG args = new NET_VALIDATE_PASSWORD_RESET_INPUT_ARG();
args.UserAccountName = Username; //some policies check that your password cannot contain your username
args.ClearPassword = SecureStringToString(password);
PNET_VALIDATE_OUTPUT_ARG res;
DWORD le = NetValidatePasswordPolicy(null, null, NetValidatePasswordReset, #args, {out}Pointer(res));
if (le <> NERR_Success)
throw new WindowsException(le); //
return res.ValidationStatus;
}

The complexity policy is that it must contain three out of five of these types:
Upper case letters
Lower case letters
Digits
Non-alphanumeric characters: ~!##$%^&*_-+=`|(){}[]:;"'<>,.?/
Unicode characters that are alphabetics but not uppercase or lowercase.
It also can't be the sAMAccountName or displayName (or parts of). You can read about it here. The other password policy rules are in adjacent documents.
You could try setting it and catch exceptions but from memory I don't think it tells you what's wrong with the password, just that it doesn't meet the requirements.

Related

What is the logic is MembershipUser.ResetPassword() method

Normally, ResetPassword(string passwordAnswer) returns a new password for the membership user. ChangePassword(string oldPassword,string newPassword) takes two parameters: old and new password. I'm OK with that but in the code below:
string pwd = mu.ResetPassword(k.SecretAnswer);
mu.ChangePassword(pwd, k.password);
return RedirectToAction("Login");
According to this code, pwd is contains old password, but ResetPassword() method returns a new random password. So how can pwd represent old password? Shouldn't the ResetPassword() method return a new password? What am I missing? What is the logic is behind?
Reset password created a password that must be reset upon first usage. It is used more as a token (in the auth realm) than an actual password.
So when you called mu.ChangePassword(pwd, k.password);, you "exchanged" the pwd token for a "normal" password.
If you had skipped changing the password in the line above and tried to log in using pwd from the Reset method it would not have succeeded.
The UI would force you to change your password and then login with the new password.
This is designed so a user is the only one to have ever seen their password in plain text (ie unencrypted).
EDIT: What exactly is a token and what is the difference between a token and a password?
Short answer(s):
A password can be used multiple times while a token can only be used once.
A password is verified while a token is redeemed.
A password requires verification for each use. A token requires verification before it is given.
While both tokens and passwords are used to gain access, it's how they are used that differentiates them.
Let's try some real-world examples (granted these examples don't align 100% with our use case, but I believe they could help).
The PIN for your ATM card is a password because:
it is secret
it is verified each time you use it
you can use it over and over
If you take a suit to the dry cleaners, they hand you a ticket (with a number) that you will use to get your suit back. That's a token because:
You must physically possess the ticket to get your suit back
If you have the ticket, you get the suit. No questions asked. You proved it was your suit when you dropped it off.
Once you use it, the ticket is worthless.

Storing and comparing multiple passwords with ServiceStack

I'm attempting to create a password expiration function in my application. Passwords are already set up as well as authentication and changing passwords. Now I want to prompt the user to change their password after x amount of time.
When the user goes to change their password and my Angular frontend makes that request I want my C# Service Stack API to compare the given new password with the current password and the password before that to check for duplication.
I'm not worried about slight variations. If the user submits the same password but with one extra character for example, that's fine. I want to be an simple as possible to start.
The passwords are stored in a MS SQL Server in two columns Salt varchar(8000) and PasswordHash varchar(8000). I've got everything set up I'm just very confused on how to compare the hashed password with the string provided by the User. Then save the old password in a new hashed column. I've been searching the web and SOF for three days now and I haven't found anything. Any guidance would be greatly appreciated.
Following on #Fildor comment, you'll need to create an audit history of password changes containing the hashes of existing passwords. From ServiceStack v5+ ServiceStack switched to use the same PBKDF2 password hashing algorithm ASP.NET Identity v3 uses which stores the password hash + salt + iterations + algorithm version in a single PasswordHash field on UserAuth table, so your password audit history table only needs a single column to store the existing password hash.
The password hashing algorithm is available from the IPasswordHasher dependency, which you can use in your Service implementation like:
public IPasswordHasher PasswordHasher { get; set; }
public object Any(AllowPassword request)
{
var passwordHashes = MyRepo.GetExistingUserPasswords(GetSession().UserAuthId);
foreach (var passwordHash in passwordHashes)
{
if (PasswordHasher.VerifyPassword(passwordHash, request.Password, out var neeedsRehash)
throw new ArgumentException("Can't use existing password", nameof(request.Password));
}
return new AllowPasswordResponse();
}

How to check WebSecurity 'Password Reset Token ID' validity?

I've been looking for a method from WebSecurity library to check the validity of the reset password token before open a reset password page for users, but unfortunately no luck!
the only thing that I've found so far is
WebSecurity.ResetPassword('new password', 'token id');
This method resets the password using a new password and if the token is not valid it will return a false value which is good but in my case I need to only check the validity of the ResetTokenId not resetting a password and that's it.
So I was wondering if anyone knows any other methods or extensions that could help me with checking the validity of the token id?
Thanks
Use WebSecurity.GetUserIdFromPasswordResetToken to find a matching user ID. If the value is -1, you don't have a valid token.

how to change the password in membership with oldpassword checking

I developed a web application. It has a login form using ASP.NET membership. Now I need to add a form allowing to change the password. Before a new password can be set, the old password must be entered by the user.
How can I check if the old password is valid?
// checking if the old password is correct
if (Membership.ValidateUser(username, oldPassword))
{
// setting a new password
string newPassword = MembershipUser.ResetPassword();
}
Membership.ValidateUser
Membership.ResetPassword
if The User logged In then you have the User Id
so retrieve all user Information like user name password using It.
now you Can just ask User to enter his old password now match this two if both matched then change the password with new One.
Use the ChangePassword control.
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.changepassword.aspx

How to Implement Password Resets?

I'm working on an application in ASP.NET, and was wondering specifically how I could implement a Password Reset function if I wanted to roll my own.
Specifically, I have the following questions:
What is a good way of generating a Unique ID that is hard to crack?
Should there be a timer attached to it? If so, how long should it be?
Should I record the IP address? Does it even matter?
What information should I ask for under the "Password Reset" screen ? Just Email address? Or maybe email address plus some piece of information that they 'know'? (Favorite team, puppy's name, etc)
Are there any other considerations I need to be aware of?
NB: Other questions have glossed over technical implementation entirely. Indeed the accepted answer glosses over the gory details. I hope that this question and subsequent answers will go into the gory details, and I hope by phrasing this question much more narrowly that the answers are less 'fluff' and more 'gore'.
Edit: Answers that also go into how such a table would be modeled and handled in SQL Server or any ASP.NET MVC links to an answer would be appreciated.
EDIT 2012/05/22: As a follow-up to this popular answer, I no longer use GUIDs myself in this procedure. Like the other popular answer, I now use my own hashing algorithm to generate the key to send in the URL. This has the advantage of being shorter as well. Look into System.Security.Cryptography to generate them, which I usually use a SALT as well.
First, do not immediately reset the user's password.
First, do not immediately reset the user's password when they request it. This is a security breach as someone could guess email addresses (i.e. your email address at the company) and reset passwords at whim. Best practices these days usually include a "confirmation" link sent to the user's email address, confirming they want to reset it. This link is where you want to send the unique key link. I send mine with a link like: example.com/User/PasswordReset/xjdk2ms92
Yes, set a timeout on the link and store the key and timeout on your backend (and salt if you are using one). Timeouts of 3 days is the norm, and make sure to notify the user of 3 days at the web level when they request to reset.
Use a unique hash key
My previous answer said to use a GUID. I'm now editing this to advise everyone to use a randomly generated hash, e.g. using the RNGCryptoServiceProvider. And, make sure to eliminate any "real words" from the hash. I recall a special 6am phone call of where a woman received a certain "c" word in her "suppose to be random" hashed key that a developer did. Doh!
Entire procedure
User clicks "reset" password.
User is asked for an email.
User enters email and clicks send. Do not confirm or deny the email as this is bad practice as well. Simply say, "We have sent a password reset request if the email is verified." or something cryptic alike.
You create a hash from the RNGCryptoServiceProvider, store it as a separate entity in an ut_UserPasswordRequests table and link back to the user. So this so you can track old requests and inform the user that older links has expired.
Send the link to the email.
User gets the link, like http://example.com/User/PasswordReset/xjdk2ms92, and clicks it.
If the link is verified, you ask for a new password. Simple, and the user gets to set their own password. Or, set your own cryptic password here and inform them of their new password here (and email it to them).
Lots of good answers here, I wont bother repeating it all...
Except for one issue, which is repeated by almost every answer here, even though its wrong:
Guids are (realistically) unique and statistically impossible to guess.
This is not true, GUIDs are very weak identifiers, and should NOT be used to allow access to a user's account.
If you examine the structure, you get a total of 128 bits at most... which is not considered a lot nowadays.
Out of which the first half is typical invariant (for the generating system), and half of whats left is time-dependant (or something else similar).
All in all, its a very weak and easily bruteforced mechanism.
So don't use that!
Instead, simply use a cryptographically strong random number generator (System.Security.Cryptography.RNGCryptoServiceProvider), and get at least 256 bits of raw entropy.
All the rest, as the numerous other answers provided.
First, we need to know what you already know about the user. Obviously, you have a username and an old password. What else do you know? Do you have an email address? Do you have data regarding the user's favorite flower?
Assuming you have a username, password and working email address, you need to add two fields to your user table (assuming it is a database table): a date called new_passwd_expire and a string new_passwd_id.
Assuming you have the user's email address, when someone requests a password reset, you update the user table as follows:
new_passwd_expire = now() + some number of days
new_passwd_id = some random string of characters (see below)
Next, you send an email to the user at that address:
Dear so-and-so
Someone has requested a new password for user account <username> at <your website name>. If you did request this password reset, follow this link:
http://example.com/yourscript.lang?update=<new\_password\_id>
If that link does not work you can go to http://example.com/yourscript.lang and enter the following into the form: <new_password_id>
If you did not request a password reset, you may ignore this email.
Thanks, yada yada
Now, coding yourscript.lang: This script needs a form. If the var update passed on the URL, the form just asks for the user's username and email address. If update is not passed, it asks for username, email address, and the id code sent in the email. You also ask for a new password (twice of course).
To verify the user's new password, you verify the username, email address, and the id code all match, that the request has not expired, and that the two new passwords match. If successful, you change the user's password to the new password and clear the password reset fields from the user table. Also be sure to log the user out/clear any login related cookies and redirect the user to the login page.
Essentially, the new_passwd_id field is a password that only works on the password reset page.
One potential improvement: you could remove <username> from the email. "Someone has request a password reset for an account at this email address...." Thus making the username something only the user knows if the email is intercepted. I didn't start off that way because if someone is attacking the account, they already know the username. This added obscurity stops man-in-the-middle attacks of opportunity in case someone malicious happens to intercept the email.
As for your questions:
generating the random string: It doesn't need to be extremely random. Any GUID generator or even md5(concat(salt, current_timestamp())) is sufficient, where salt is something on the user record like timestamp account was created. It has to be something the user can't see.
timer: Yes, you need this just to keep your database sane. No more than a week is really necessary but at least 2 days since you never know how long an email delay might last.
IP Address: Since the email could be delayed by days, IP address is only useful for logging, not for validation. If you want to log it, do so, otherwise you don't need it.
Reset Screen: See above.
A GUID sent to the email address of record is likely enough for most run-of-the-mill applications - with timeout even better.
After all, if the users emailbox has been compromised(i.e. a hacker has the logon/password for the email address), there is not much you can do about that.
You could send an email to user with a link. This link would contain some hard to guess string (like GUID). On server side you would also store the same string as you sent to user. Now when user presses on link you can find in your db entry with a same secret string and reset its password.
1) For generating the unique id you could use Secure Hash Algorithm.
2) timer attached? Did you mean an Expiry for the reset pwd link?
Yes you can have an Expiry set
3) You can ask for some more information other than the emailId to validate..
Like date of birth or some security questions
4) You could also generate random characters and ask to enter that also along with the
request.. to make sure the password request is not automated by some spyware or things like that..
I think Microsoft guide for ASP.NET Identity is a good start.
https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity
Code that I use for ASP.NET Identity:
Web.Config:
<add key="AllowedHosts" value="example.com,2.example" />
AccountController.cs:
[Route("RequestResetPasswordToken/{email}/")]
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> GetResetPasswordToken([FromUri]string email)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
Logger.Warn("Password reset token requested for non existing email");
// Don't reveal that the user does not exist
return NoContent();
}
//Prevent Host Header Attack -> Password Reset Poisoning.
//If the IIS has a binding to accept connections on 80/443 the host parameter can be changed.
//See https://security.stackexchange.com/a/170759/67046
if (!ConfigurationManager.AppSettings["AllowedHosts"].Split(',').Contains(Request.RequestUri.Host)) {
Logger.Warn($"Non allowed host detected for password reset {Request.RequestUri.Scheme}://{Request.Headers.Host}");
return BadRequest();
}
Logger.Info("Creating password reset token for user id {0}", user.Id);
var host = $"{Request.RequestUri.Scheme}://{Request.Headers.Host}";
var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = $"{host}/resetPassword/{HttpContext.Current.Server.UrlEncode(user.Email)}/{HttpContext.Current.Server.UrlEncode(token)}";
var subject = "Client - Password reset.";
var body = "<html><body>" +
"<h2>Password reset</h2>" +
$"<p>Hi {user.FullName}, please click this link to reset your password </p>" +
"</body></html>";
var message = new IdentityMessage
{
Body = body,
Destination = user.Email,
Subject = subject
};
await UserManager.EmailService.SendAsync(message);
return NoContent();
}
[HttpPost]
[Route("ResetPassword/")]
[AllowAnonymous]
public async Task<IHttpActionResult> ResetPasswordAsync(ResetPasswordRequestModel model)
{
if (!ModelState.IsValid)
return NoContent();
var user = await UserManager.FindByEmailAsync(model.Email);
if (user == null)
{
Logger.Warn("Reset password request for non existing email");
return NoContent();
}
if (!await UserManager.UserTokenProvider.ValidateAsync("ResetPassword", model.Token, UserManager, user))
{
Logger.Warn("Reset password requested with wrong token");
return NoContent();
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);
if (result.Succeeded)
{
Logger.Info("Creating password reset token for user id {0}", user.Id);
const string subject = "Client - Password reset success.";
var body = "<html><body>" +
"<h1>Your password for Client was reset</h1>" +
$"<p>Hi {user.FullName}!</p>" +
"<p>Your password for Client was reset. Please inform us if you did not request this change.</p>" +
"</body></html>";
var message = new IdentityMessage
{
Body = body,
Destination = user.Email,
Subject = subject
};
await UserManager.EmailService.SendAsync(message);
}
return NoContent();
}
public class ResetPasswordRequestModel
{
[Required]
[Display(Name = "Token")]
public string Token { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}

Categories

Resources