I am trying to verify a hashed password that is hashed in the register page, when I try to verify the hashed password with a entered password from the login, false is always returned.
I am hashing the password so:
string hashPassword = BCrypt.Net.BCrypt.HashPassword(Password);
The hashpassword is then saved to the database.
I try to verify the password so:
bool validPassword = BCrypt.Net.BCrypt.Verify(ProvidedPassword, StoredPassword);
Debug.WriteLine(validPassword);
if (validPassword)
{
Debug.WriteLine(ProvidedPassword + " is valid");
}
else
{
Debug.WriteLine("Passwords do not match");
}
I am using this source from github.
I have tried multiple methods and still always returns a false value.
I found the issue, my stored procedure parameters did not match my tables paremeters
Related
I currently have a system where if a user has forgotten their password, they can reset it by clicking on a forgot password link. They will be taken to a page where they enter in their username/email and then an email will be sent to the user, I wanted to know how can I implement a password reset link in the email so once the user clicks on the link he/she is taken to a page which will allow them to reset their password.
This is the code in my controller
public ActionResult ForgotPassword()
{
//verify user id
string UserId = Request.Params ["txtUserName"];
string msg = "";
if (UserId == null)
{
msg = "You Have Entered An Invalid UserId - Try Again";
ViewData["ForgotPassword"] = msg;
return View("ForgotPassword");
}
SqlConnection lsql = null;
lsql = DBFactory.GetInstance().getMyConnection();
String sqlstring = "SELECT * from dbo.[USERS] where USERID = '" + UserId.ToString() + "'";
SqlCommand myCommand = new SqlCommand(sqlstring, lsql);
lsql.Open();
Boolean validUser;
using (SqlDataReader myReader = myCommand.ExecuteReader())
{
validUser = false;
while (myReader.Read())
{
validUser = true;
}
myReader.Close();
}
myCommand.Dispose();
if (!validUser)
{
msg = "You Have Entered An Invalid UserId - Try Again";
ViewData["ForgotPassword"] = msg;
lsql.Close();
return View("ForgotPassword");
}
//run store procedure
using (lsql)
{
SqlCommand cmd = new SqlCommand("Stock_Check_Test.dbo.RESET_PASSWORD", lsql);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter paramUsername = new SqlParameter("#var1", UserId);
cmd.Parameters.Add(paramUsername);
SqlDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
if (Convert.ToInt32(rdr["RC"]) == 99)
{
msg = "Unable to update password at this time";
ViewData["ForgotPassword"] = msg;
lsql.Close();
return View("ForgotPassword");
}
}
}
msg = "new password sent";
ViewData["ForgotPassword"] = msg;
lsql.Close();
return View("ForgotPassword");
}
This is my current stored procedure which sends the user an email
ALTER PROCEDURE [dbo].[A_SEND_MAIL]
#var1 varchar (200), -- userid
#var2 varchar (200) -- email address
AS
BEGIN
declare #bodytext varchar(200);
set #bodytext = 'Password Reset for user: ' +#var1 + ' #' + cast (getDate() as varchar) + ' ' ;
EXEC msdb.dbo.sp_send_dbmail
#profile_name='Test',
#recipients=#var2,
#subject='Password Reset',
#body=#bodytext
END
GO
Create a table that has a structure like
create table ResetTickets(
username varchar(200),
tokenHash varbinary(16),
expirationDate datetime,
tokenUsed bit)
Then in your code when the user clicks the reset password button you will generate a random token then put a entry in that table with the hashed value of that token and a expiration date of something like DATEADD(day, 1, GETDATE()) and appends that token value on the url you email to the user for the password reset page.
www.example.com/passwordReset?username=Karan&token=ZB71yObR
On the password reset page you take the username and token passed in, hash the token again then compare that with the ResetTickets table, and if the expiration date has not passed yet and the token has not been used yet then take the user to a page that lets them enter a new password.
Things to be careful about:
Make sure to expire the token, don't let a email from two years ago reset the password.
Make sure to mark the token as used, don't let other users of the computer use the browser's history to reset other users passwords.
Make sure you generate the random token safely. Don't use Rand and use it to generate the token, two users who reset at the same time would get the same token (I could reset my password and your password at the same time then use my token to reset your account). Instead make a static RNGCryptoServiceProvider and use the GetBytes method from that, the class is thread safe so you don't need to worry about two threads using the same instance.
Be sure to parameterize your queries. In your current code if I typed in the userid '; delete dbo.[USERS] -- it would delete all the users in your database. See the linked SO post for more info on how to fix it.
Be sure you hash the token, your passwordReset page only accepts the unhashed version, and you never store the unhashed version anywhere (including email logs of outgoing messages to users). This prevents an attacker who has read access to the database from making a token for some other user, reading the value that was sent in the email, then sending the same value himself (and perhaps getting access to an administrator user who can do more stuff than just read values).
here are 2 alternatives using HMAC or JWT (which i think provide better, more secure, email URLS)
https://neosmart.net/blog/2015/using-hmac-signatures-to-avoid-database-writes/
https://www.smashingmagazine.com/2017/11/safe-password-resets-with-json-web-tokens/
Is there any way to get the full message response from the changepassword method? I need to find out why it's returning false. It never has in the past, the current password is correct and so is the emailresponse variable.
MembershipUser u = Membership.GetUser(emailresponse);
bool changed = u.ChangePassword("Password~123", txtPassword.Text);
if (changed){
//code emitted
}
I'm connecting to OpenLDAP with C#, and when I pass in my username and password, I have to pass them into my LdapConnection object as cn=Username, Password. If I just pass in username and password my call to Bind fails. Why do I have to do that? Is something misconfigured on my OpenLDAP server?
It's just a byproduct of the implementation. Novell's eDirectory solution takes a very similar approach, and I use the same Novell.Directory.Ldap code to handle bind requests to both eDirectory and OpenLDAP. Now obviously, the users themselves shouldn't have to enter their entire CN when authorizing - we can just issue a search for them, based of thier UID :
//Setup the initial bind for the admin user
var lc = new LdapConnection();
lc.SecureSocketLayer = SSL;
lc.UserDefinedServerCertValidationDelegate += delegate { return true; };
lc.Connect(ServerName, Port);
lc.Constraints.TimeLimit = Timeout;
lc.Bind(AdminUsername, AdminPassword);
Now I just filter for the user, and bind using their distinguished name, or full container name (CN) :
//Ex. (uid=jsmith)
string filter = config.LdapAuth.LdapFilter.Replace("{{uid}}", username);
//Find the user we're trying to authorize
var lsc = lc.Search(config.LdapAuth.LdapDomain, LdapConnection.SCOPE_SUB, filter, null, false);
if (lsc.hasMore())
{
LdapEntry nextEntry = lsc.next();
//Check the Entries DN so we can properly bind
lc.Bind(nextEntry.DN, Password);
}
This was the most widely used approach I could find, and it's worked quite well so far.
I know my Account Username and password. I am able to login to any PC on the domain.
Console.WriteLine("User Name: " + userName + " Password: " + tb.Text.ToString().Trim());
System.DirectoryServices.AccountManagement.PrincipalContext pc = new System.DirectoryServices.AccountManagement.PrincipalContext(ContextType.Domain, "DOMAIN.TLD");
// validate the credentials
bool validatedOnDomain = pc.ValidateCredentials(userName, tb.Text.ToString().Trim());
return validatedOnDomain;
This method keeps returning false.
Am I doing something wrong here? I also know what my password is. Any assitance would be great!
tb -> TextBox where the password is being inputted. I remove all white spaces and trimmed it (in case a user screws up)
Can you try this :
bool validatedOnDomain = pc.ValidateCredentials(userName, tb.Text.ToString().Trim(), ContextOptions.Negotiate);
You just have to Specifie the options that are used for binding to the server.
I currently use LogonUser() to authenticate my user's username and password on my local domain at the office and it works great for what i need it to do.
Since I developed the app I now need to make it work over my VPN. It seems LogonUser() will not work with REMOTELY validating credentials. Or will it? Is it possible to use LogonUser() to validate a user's credentials on a REMOTE domain account?
I have read in some places that using LOGON32_LOGON_NEW_CREDENTIALS for the 4th param (login type) and LOGON32_PROVIDER_WINNT50 for the 5th param (provider) would do the trick. But every time I try that I ALWAYS get success... I can supply a bogas user and pass and it will work every time :(.
Ideas?
Edit - Added Notes
Tried to use this function but I kept getting the exception telling me the user/pass was bad.
public bool Win2kCredentialsIsValid(string domain, string username, string password)
{
string adPath = "LDAP://" + domain + "/rootDSE";
DirectoryEntry adRoot = new DirectoryEntry(adPath, domain + "\\" + username, password, AuthenticationTypes.ReadonlyServer);
try
{
object o = adRoot.Properties["defaultNamingContext"];
}
catch
{
return false;
}
return true;
}
--
Edit - Added More Notes
OK so I tried yet another example just to get it to work and started down this path, and there are a few things to note...
MyServerHostName is exactly that, my server's hostname. EX: 'Server01'.
My domain name in this example is 'MyDomain.local'
So that makes my FQN for the server 'Server01.MyDomain.local'
I tried to make this work and got the following error...
The supplied context type does not match the server contacted. The server type is Domain.
This errored out at : var context = new PrincipalContext(ContextType.ApplicationDirectory, "MyServerHostName:389", "DC=MyDomain,DC=local"))
private bool CheckADCredentials()
{
bool bResults;
using (var context = new PrincipalContext(ContextType.ApplicationDirectory,
"MyServerHostName:389",
"DC=MyDomain,DC=local"))
{
var username = "firstname.lastname";
var email = "firstname.lastname#MyServerHostName";
var password = "123456";
var user = new UserPrincipal(context)
{
Name = username,
EmailAddress = email
};
user.SetPassword(password);
user.Save();
if (context.ValidateCredentials(username, password, ContextOptions.SimpleBind))
{
bResults = true;
}
else
{
bResults = false;
}
user.Dispose();
}
return bResults;
}
I ended up going with a different solution. Instead of trying to validate a user's account on a domain that my PC was not connected to I ended up caching my domain credentials in the database and just built a salted MD5 type encrypt function so it would make it hard .. er.. for someone to crack it. ;)
Now I just validate against cached credentials in the database when working remotely... It just required the user to first login on the domain but then the user can use it remotely day and night. ;)
Thanks!