Trying to encrypt a random key for password reset functionality - c#

I am trying to encrypt a random token to be stored in the user table when a user uses the forgot password field by entering their username. It will also send out an e-mail with a url to the change user password page. This url will have a query string param called 'key'.
i.e. www.mysite.com/Changepassword?key=xfsdfsdffsdfiughjksdf.
Once the user clicks the link and they are on this page. I have a function that will get a user by ResetToken. If it finds a user then proceed.
I need advice on a few things:
What kind of random token/encryption technique should I use to
ensure that no one can go to the Changepassword page and guess a key
and be able to change a users password.
How will I handle making this key url friendly?

If you want to be super-secure, using the cryptographically secure random number generator would work. And converting to hex is the easiest way to ensure it's URL-safe:
byte[] bytes = new byte[8];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(bytes);
}
string key = string.Join("", bytes.Select(b => b.ToString("X2")));
EDIT Keep in mind that e-mail is potentially insecure, so it's possible (however unlikely) that your message can be intercepted by a malicious third-party before reaching the recipient.

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

Encrypt passwords existing in Database sql windowsForms

I have a database of logins and passwords. I wouldn't like that anyone who has access to the database can see everybody's password. How can I encrypt the passwords in the database?
In other words, I want the fields pwd (password) to be encrypted in the database but it is automatically decrypted when I enter it in the LoginForm.
I have found a method that encrypt the strings input but it doesn't solve my issue.
static string Encrypt(string value)
{
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
UTF8Encoding utf8 = new UTF8Encoding();
byte[] data = md5.ComputeHash(utf8.GetBytes(value));
return Convert.ToBase64String(data);
}
}
private void BtnEncrypt_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtPass.text))
{
MessageBox.Show("Please enter your password !");
}
texResult.Text=Encrypt(txtPass.Text);
}
Please, can somebody help me.
Thanks in advance.
You can Encrypt your password using your Encrypt function and store the Encrypted password in your database.
But Decrypting the password, is not a good option. Password Encryption should be one way.
To check whether the password is available in your database, you can Encrypt the password entered by user by using the same Encrypt function, then match that Encrypted password to encrypted password you have in your database.
Thanks
It is easy to muddle encryption with hashing. What you are asking about is encryption - encryption lets you turn your password into an apparently random sequence of characters which can then be decrypted to get the original password back. What you should be using (and some have suggested) is hashing.
There are lots of examples of how to do encryption/decryption on the net, just search. This is the first one that came up for me: http://www.codeproject.com/Articles/14150/Encrypt-and-Decrypt-Data-with-C Tempting as it is to copy and paste the code from there, I won't because this isn't what you should be doing. For storing user passwords in a database it is much better to use password hashing (with salt) than to store encrypted passwords. Why? because then if your system is hacked it is impossible for an attacker to recover people's passwords - all your accounts might still be compromised but given that people often use the same password for more than one system you won't be compromising your users.
A hash is a one way function, so you can't get the original password back. When someone wants to login you simply generate a hash and then compare it with the one you have stored in the database. If you want to read more about this and why you should be using it then this is a good start: https://crackstation.net/hashing-security.htm If you would like to jump in and get some working code then have a look at Hash and salt passwords in C#.
You can use any complex cryptography technique to encrypt a password and send the password key to be saved in database for corresponding user.
Now when the client tries to login and enters password, sends it to server.
From the server you can again convert the login details and compute the hash and finally send to a stored procedure to compare. If the two strings match, you return true else false as for authentication.
using System.Security.Cryptography;
...
...
...
private const string _alg = "HmacSHA256";
private const string _salt = "rz8LuOtFBXphj9WQfvFh"; // Generated at https://www.random.org/strings
public static string GenerateToken(string username, string password)
{
string hash = string.Join(":", new string[] { username, password });
using (HMAC hmac = HMACSHA256.Create(_alg))
{
hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
hash = Convert.ToBase64String(hmac.Hash);
}
return Convert.ToBase64String(Encoding.UTF8.GetBytes(hash));
}
public static string GetHashedPassword(string password)
{
string key = string.Join(":", new string[] { password, _salt });
using (HMAC hmac = HMACSHA256.Create(_alg))
{
// Hash the key.
hmac.Key = Encoding.UTF8.GetBytes(_salt);
hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hmac.Hash);
}
}
MD5 is not secure anymore.
When a user register to use your application, hash the password with SHA512 bit with salt. You can find like PWDTK nuget package which we can easily use. Password is what we don't need to know what it means but just plays a secure role. Like some person commented above, when the user try to log-in after user registration, just encrypt the user's input(password) and compare it with that registered in SQL database. Password must be one-way.
After the login result comes up success or fail, the role of password is finished.
As of Winform cases, you need to deeply consider to secure the connectionstring to connect to SQL database. One possible option might be WCF middleware between Winform application and SQL database.
And for last but very importantly, you must use SSL for secure communication.
It seems you might consider these at later stages.

C# ASP.NET Identity

I have a scenario here whereby when a user wants to reset a password, the system will have to send a temporary random generated password to the user by email. I tried storing the temporary password into a new column in the database but I am not really sure about whether this approach works well. Some people recommend using token such as below:
string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
However, I am really new to ASP.NET and I am not familiar with token. How do I compare the temporary generated token with the token in the database?
Another method that I found to implement this is to have a Membership.GeneratePassword function that generates a random string of characters:
model.temppwd = Membership.GeneratePassword(10, 1);
Can anybody provide me an ideal way to implement this functionality with some example? Thank you!
In our project we used
Guid.NewGuid();
and sent the email containing the link to the recover password action (MVC) as a query string: https://yoursite.com/account/reset/?code=your_guid_code
Example:
ResetPassword resetPassword = new resetPassword();
resetPassword.Code = Guid.NewGuid();
string strLink = string.Format("{0}", actionUrl + "?code="+ resetPassword.Code);`
And now you can use the strLink to send with your e-mail. You'll need to store the Guid in a database table alongside with the userId, so that you can implement the resetting procedure. When the user clicks the link from your email he'll get in a form / view that asks for a new password. Also you'll want to add an extra column to that table in order to provide an expiration limit for that code. If the user clicks the link in the e-mail and the code expired you'll have to inform the user and send another e-mail with another code.

How to generate a ciphered html URL in C#?

Creating an intranet application, in which URL will be sent to the user via Email(outlook). This URL should be generated using cipher(need a randon URL based on his Email/Emp ID) and through this URL user should be able to login into account. The link should also have time validity. Example: link expires after 7 days.
What I have?
User's Email and Employee ID.
Any pointers, how to achieve the above task in C#? or Any other idea, because I just need to User to access application for certain time using Email only.
It seems that you have gotten it a bit mixed up. The URL cannot be encrypted with the ID or emai. There is little point in using as a key the Employee Id or the email since they are public information.
What you could do is create a random string for the URL and just use the two pieces of information to generate the string. However, once they visit the URL you will need to ask them for a secret password in order to log in. Just by visiting the URL and the inputting their email you achieve nothing in terms of security. It is trivial to break such a system by bruteforce if you have access to the emails/employee ids (which you must assume all have).
For the URL based on the email you can xor the two strings and send the output. Then xor the output with the email and get the url.
public static string xorIt(string email, string url)
{
StringBuilder sb = new StringBuilder();
for(int i=0; i < input.Length; i++)
sb.Append((char)(url[i] ^ email[(i % key.Length)]));
String result = sb.ToString ();
return result;
}
a way to check for validity is to store the url in a database along with a timestamp and check at the time of login if it still valid.
There are some caveats with the above approach (e.g. xor always produces the same output for the same two strings but this can be solved if you add a random string and store that string in the table above).
You can't lock this down to their e-mail only, because they could just copy & paste the URL into a browser.
Essentially to get the time-sensitive behaviour, you need to have the following:
A database table (or some form of storage) to indicate that this user has been issued a token, and when that token expires
A means to generate the cipher-url from the user's e-mail address.
Generating the URL part is easy, in pseudocode you could envision it being as such:
var user = GetUser("John.Doe#gmail.com");
var token = EncryptAndHashEmailAddress(user);
var entry = new TokenTableEntry(token:token, expires:DateTime.Now.AddDays(7), User: user.Id);
var url = #"https://www.mywebsite.com/login.aspx?token=" + token;
Then when the user clicks the link and hits whatever handles the login request:
var token = GetQueryParameter(0); // Get first query parameter
var entry = TokenTable.Element(e => e.Token == token);
TokenTable.Remove(entry);
if (DateTime.Now > entry.Expires)
{
// Their token has expired
GoTo("TokenExpired.aspx");
return;
}
LoginController.UserLoggedIn(entry.Id);
This is very rough psuedocode, but it should put you on the right tracks.

Categories

Resources