Hello I need some help with this issue after I search the solution and I have not found yet,
I want to compare 2 hash password with bcrypt of the same password,
how do I do it?
for example:
I have these 2 hash password that came from the same password in bcrypt:
var password = E#Js#07Do=U$
var hash1 = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
var hash2 = $2a$10$mgApOcRIp7RSK3lRIIlQ5e/GjVFbxAFytGAEc0Bo17..r8v2pPR22
// that's not working for me
bcrypt.compare(passwordHash, userPasswordLoginHash, function(err, isMatch) {
if (err) throw err;
if(isMatch){
console.log('correct password!')
}
callback(null, isMatch);
});
how can i compare them, to determine that they came from the same password, by using bcryptjs npm package?
This is impossible by design - as a core security property of true password hashing.
If you could compare two password hashes without knowing the original password, then if an attacker cracked one password on the system, they would instantly know the passwords of all users who are using that password, without any additional work. It should be immediately obvious why this would be a bad thing.
For example, if passwords were stored using a hash inappropriate for password storage (such as MD5), then if 50 users had a password of 'password', then all of their hashed passwords would have the identical MD5 hash ('5f4dcc3b5aa765d61d8327deb882cf99'), and cracking one of them would reveal the password of all 50 users.
You can't do that with a modern password hash like bcrypt. The only way to "compare" two modern password hashes is to know the plaintext in advance, and then apply the algorithm using the salt in each hash. And even if two users have the same password, the attacker has to perform the same expensive computation to crack each of them independently, because the unique salts make each hash unique.
More generally - and this may sound a bit bold - but there is no legitimate use case for any system or administrator to ever compare two different users' passwords. User passwords should be 100% independent and 100% opaque to the system once stored. If a system or business case requires this kind of comparison, it should be redesigned to eliminate that requirement.
"With bcrypt lib you compare plain text password to the one hashed using the same lib."
The problem is with a micro services architecture, that is very insecure. If I have a front end passing an unhashed password to the backend, the unhashed password is getting logged (possibly in multiple places) before it gets compared against the hash in the DB on the system backend.
With bcrypt lib you compare plain text password to the one hashed using the same lib.
Say you hashed a password
const myPlaintextPassword = 'E#Js#07Do=U$'
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
// example output, taking your hash
// hash = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
});
You compare like:
// db query, get hashed password, found hash
// hash = $2a$10$fKAyjaG0pCkisZfRpKsBxursD6QigXQpm1TaPBDZ4KhIZRguYPKHe
// User input again:
const myPlaintextPassword = 'E#Js#07Do=U$'
bcrypt.compare(myPlaintextPassword, hash, function(err, res) {
// res is true as the original password is the same
// res == true
});
For a bit extra security you can encrypt the password in the front-end and decrypt and compare in the back-end
Related
I am sending a token to user email like below and using bcrypt for this as an encrypt/decrypt mechanism.
const token = await bcrypt.hash(joinDate, 10);
When the user clicks on the link in email, I get the above token back as that token is a part of
/api/unsubscribe?userId="abcd"&token="token_that_was_generated_using_bcrypt_and_sent_to_user"
const {userId, token} = req.query;
In the api, I am comparing joinDate obtained from database vs token sent by req.query but it never matches.
const joinDate = user.joinDate.toString();
const tokenValidated = await bcrypt.compare(joinDate, token)//this is always false although us generated from same joinDate field
Why is tokenValidated always false although it was generated using the same field joinDate?
Your use of bcrypt is not secure. Anyone can brute-force a few hundred or a few thousand dates and cause any user to become unsubscribed.
I assume your motivation is to avoid having to create a new table in your database and store a random token for every email you've sent out.
If so, the proper tool to use is HMAC.
Your URL should be of the form: /api/unsubscribe?userId=abcd&mac=....
Come up with a secret key known only to your server. This secret key will be used to create and authenticate all unsubscribe requests. Only use this key for authenticating unsubscribe requests.
Perform HMAC-SHA512 on the user id, with the HMAC output truncated to 128 bits. Then base64-encode the 128 bits and set it as the mac parameter in the URL.
HMAC means to create a hash-based message authentication code, which will confirm to your server that it must have created and emailed out that 'mac'.
Now, your server can authenticate each response, because only someone with knowledge of the server's secret key can produce a valid unsubscribe link.
Your logic of using bcrypt for hash and verifying on the other end should work. The only possible thing that can prevent it from working is either:
1 - the joinDate here: const token = await bcrypt.hash(joinDate, 10);
is not ===
to joinDate here:const tokenValidated = await bcrypt.compare(joinDate, token)
( maybe differes in ", ' or different format orsomething )
2 - or the token is kinda different in ' or " when passing through / read from queryparam; (e.g. you token is going "'token_that_was_generated_using_bcrypt_and_sent_to_user'" for comparison.)
I honestly tried my best to find the answer here or anywhere else.
Bcrypt documentation states that there are 2 techniques to hash/salt a password:
Technique 1 (generate a salt and hash on separate function calls):
bcrypt.genSalt(saltRounds, function(err, salt) {
bcrypt.hash(myPlaintextPassword, salt, function(err, hash) {
// Store hash in your password DB.
});
});
And technique 2 (auto-gen a salt and hash):
bcrypt.hash(myPlaintextPassword, saltRounds, function(err, hash) {
// Store hash in your password DB.
});
Note that both techniques achieve the same end-result...
If they do, why do we need to add extra lines of code?
Is it just esthetical preference? Or is there any practical reason?
Thank you!
It's a common implementation in a lot of libraries where they want to use the more tedious version.
they insist that you have to pass in everything required to run the function
and they abstract the details of passing in salt and cost and versioning away in a salt string
I believe the method signature should be:
bcrypt.HashPassword("hunter2"); //using a default cost
bcrypt.HashPassword("hunter2", 15); //if we want to force a cost
But nearly every other bcrypt library does something like:
String salt = bcrypt.GenerateSalt(); //using a default cost
bcrypt.HashPassword("hunter2", salt);
String salt = bcrypt.GenerateSalt(15); //if we want to for a cost
bcrypt.HashPassword("hunter2", salt);
Because then what happens internally when they go to verify a hash, they extract the saved salt string from the stored hash:
String salt = GetSaltStringFromSavedHash(savedHash);
bcrypt.HashPassword("hunter2", salt);
And so they just love this symmetry of using HashPassword in the same way for both calls.
I disagree that any of this salt should be exposed to the user - even if it designed to be an opaque blob (i.e. $2b$15$aXN0aWxsbG92ZXlvdWtn...).
I think it should be:
HashPassword(password);
HashPassword(password, costFactor);
But that's just me; and i'm the only one.
I tried to dig in the sanitize function but i didn't find the answer that I wanted, the question is how to check if a user input doesn't contain any sql injection? i'm using Nest JS (similar to node) if you have any idea of how I can do that it would help me a lot!
Generally speaking: You can't. Any input which might be SQL injection could, conceivably, also be genuine input (at least in certain narrow circumstances).
Don't try to detect SQL injection. Instead, use placeholders where possible and proper escaping where not.
The issue is not on the NestJS side, it is on how you build the SQL query to execute.
You may use #nearform/sql that:
A simple SQL injection protection module that allows you to use ES6 template strings for escaped statements. Works with pg, mysql and mysql2 library.
It will protect you against malicious users input.
This module is battle-tested and already in production for Covid Government Tracker.
You can use prepared statements to avoid sql injection attacks
This blog post explains it quite well. for reference a snippet from the post.
function authenticate(req, res, next){
const username = req.query.username,
password = req.query.password
let preparedStatement = new sql.PreparedStatment(),
sqlQuery = "select * from users where (username = #username and password = #password)"
preparedStatement.input('username', sqlVarChar(50))
preparedStatement.input('password', sqlVarChar(50))
preparedStatement.prepare(sqlQuery)
.then(function(){
return preparedStatement.execute({username: username, password: password})
.then(function(recordset){
if(recordset.length == 1){
loggedIn = true
//successful log in
} else {
//authentication failed
}
})
})
.catch(next)
}
So I'm developing a website using php, mysql and javascript, and also 'sha512' to encrypt passwords of members using the code :
$password = filter_input(INPUT_POST, 'p', FILTER_SANITIZE_STRING);
$random_salt = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
$password = hash('sha512', $password . $random_salt);
the p value is comming from :
function formhash(form) {
var password = randomString();
var p = document.createElement("input");
form.appendChild(p);
p.name = "p";
p.type = "hidden";
p.value = hex_sha512(password.value);
password.value = "";
form.submit();
}
function randomString() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 9; i++ )
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
My idea here is to reset user password by entering their email and generate random 8 characters then send it directly to their email.
The problem I'm facing now is how to get the actual password (not encrypted) that has been generated so it can be automatically sent to the email of the member who requested to reset their password?
Good question.
First, you should never send users their passwords in plaintext. It's considered a bad security practice for a few reasons. If anyone gets access to the email, then they have the password and can hijack the user account. Second, hashing is a one-way form of encryption where you turn the password into gibberish. The big value in hashing is that the same password will always be turned into the same gibberish-- everytime. This means you can do password matching without ever storing the raw password. The reason you're supposed to hash a password and not do 2-way encryption like AES-256, is that 2-way encryption requires the creation, management, and securing of encryption keys which can be hard. Hashing is just easier and more secure for the vast majority of developers.
So how should you implement password reset if you can't send the raw password?
You send the user an email with a link to a secure reset page AND a one-time use reset token that expires within a certain window. This way, if someone get's access to the email then the window of risk is limited to the short window.
There are a variety of ways to build this yourself but an easy approach to getting a one-time use token you don't have to store or manage is to offload user management to a microservice like Stormpath where it takes care of all the user management for you-- password reset, password storage, user profiles, authentication, etc.
For password reset here's what it would look like:
User initiates password reset work on a web page
You make API call to stormpath with user's email address or username
Stormpath sends out reset email to user (your "from" address, custom HTML, etc) with a link + token. The reset token that is unique, one-time use, and expires if not used within 24 hours
User clicks on the link and lands on the reset page
You pull the token from the URL and check Stormpath for token verification
User submits new password
Stormpath sends out reset success message (your "from" address, custom HTML, etc)
You can build your own UIs in this flow so the user never knows Stormpath exists.
Now, you don't have to manage, store, or secure any passwords or reset tokens in your database.
Here's are some links to the community-managed PHP SDK.
http://docs.stormpath.com/php/quickstart/
http://docs.stormpath.com/php/product-guide/
Full Disclosure - I work at Stormpath
and also 'sha512' to encrypt passwords
You're not encrypting them, you're hashing them. A hash is a one-way function. You can't take the result of a hash function and get the original. There are many possible original chunks of data that can result in the same hash.
The whole point of hashing in this context is to be able to check passwords without ever actually storing the user's password. You shouldn't send the user their password in e-mail, as e-mail is sent over the internet unencrypted. If you must have the original pre-hashed data for some reason, you must store it before you hash it.
I use the nodejs bcrypt library for better password protection.
I am not sure i understand exactly how to use it, but i got this so far:
//A module containing this login function:
login: function(credentials,req,res) {
//"credentials" is containing email and password from login form
var query = 'SELECT password, email FROM users WHERE email = ? LIMIT 1';
client.query(query,[credentials.email], function(err, results) {
if (results[0]) {
//Compare passwords
if (bcrypt.compareSync(credentials.password, results[0].password)) {
//Set session data and redirect to restricted area
}
}
});
}
I removed all the error handling here in the example so that its easier to read the code.
1.This works and i am able to login and set the session. But is this all there is to it? Am i missing something?
2.Looks like the salt is prepended to the password when generating hash. Dont I have to save the salt in db?
Any help appreciated
Yes, this is all there is to it! The salt you generate when encrypting the password originally is used to prevent against rainbow table attacks; you do not need to persist it.