69

I have this script that encrypts a password but I don't know how to reverse it and decrypt it. This may be a very simple answer but I don't understand how to do it.

#!/usr/bin/perl
use Crypt::Eksblowfish::Bcrypt;
use Crypt::Random;

$password = 'bigtest';
$encrypted = encrypt_password($password);
print "$password is encrypted as $encrypted\n";

print "Yes the password is $password\n" if check_password($password, $encrypted);
print "No the password is not smalltest\n" if !check_password('smalltest', $encrypted);

# Encrypt a password 
sub encrypt_password {
    my $password = shift;

    # Generate a salt if one is not passed
    my $salt = shift || salt(); 

    # Set the cost to 8 and append a NUL
    my $settings = '$2a$08$'.$salt;

    # Encrypt it
    return Crypt::Eksblowfish::Bcrypt::bcrypt($password, $settings);
}

# Check if the passwords match
sub check_password {
    my ($plain_password, $hashed_password) = @_;

    # Regex to extract the salt
    if ($hashed_password =~ m!^\$2a\$\d{2}\$([A-Za-z0-9+\\.]{22})!) {
        return encrypt_password($plain_password, $1) eq $hashed_password;
    } else {
        return 0;
    }
}

# Return a random salt
sub salt {
    return Crypt::Eksblowfish::Bcrypt::en_base64(Crypt::Random::makerandom_octet(Length=>16));
}
RobEarl
  • 7,862
  • 6
  • 35
  • 50
BluGeni
  • 3,378
  • 8
  • 36
  • 64
  • 5
    That's exactly what hashing doesn't mean. You should never be able to read a password. – SLaks Aug 06 '13 at 15:41
  • 125
    You can't "decrypt" a hash, because it's not encrypted. Hashes are like hamburger. Easy to go Cow->Hamburger. But you want Hamburger->Cow. Good luck... – Marc B Aug 06 '13 at 15:42
  • Oh ok I get it, thanks! Sorry I was confused. – BluGeni Aug 06 '13 at 15:42
  • 12
    @MarcB Love that analogy, will use it in future. – Glitch Desire Aug 06 '13 at 15:49
  • Use Authen::Passphrase instead of cobbling together your own authentication scheme. See my code in http://stackoverflow.com/questions/3675917/how-can-i-encrypt-and-decrypt-passwords-in-a-perl-cgi-program – daxim Aug 06 '13 at 19:34

6 Answers6

205

You're HASHING, not ENCRYPTING!

What's the difference?

The difference is that hashing is a one way function, where encryption is a two-way function.

So, how do you ascertain that the password is right?

Therefore, when a user submits a password, you don't decrypt your stored hash, instead you perform the same bcrypt operation on the user input and compare the hashes. If they're identical, you accept the authentication.

Should you hash or encrypt passwords?

What you're doing now -- hashing the passwords -- is correct. If you were to simply encrypt passwords, a breach of security of your application could allow a malicious user to trivially learn all user passwords. If you hash (or better, salt and hash) passwords, the user needs to crack passwords (which is computationally expensive on bcrypt) to gain that knowledge.

As your users probably use their passwords in more than one place, this will help to protect them.

Glitch Desire
  • 14,632
  • 7
  • 43
  • 55
  • How do I then check the password entered is the same as the one stored in the database? – BluGeni Aug 06 '13 at 16:18
  • @BluGeni `bcrypt` maps many to one, so if the user inserts a password, just run it through the same function you used when you stored it (`encrypt_password($input)`). If the output is the same, the user [probably](http://en.wikipedia.org/wiki/Hash_collision) inserted the same password. – Glitch Desire Aug 06 '13 at 16:20
  • So do I need to save the salt in the database in this case? wouldnt that defeat the purpose of having a salt? Because right now everytime I run the script $bigtest encrypted is different because of the random salt I believe? – BluGeni Aug 06 '13 at 16:42
  • 7
    @BluGeni Yes, you need to save the salt in the database, and no, it doesn't defeat the purpose of having a salt. The salt is different for each password. What this prevents is a hacker getting (or generating) a table of checksums for every 1-8 digit password and learning 40% of your users' logins from ONE operation. Instead he has to generate this table once *per password*. – Glitch Desire Aug 06 '13 at 16:44
  • 1
    So, dear people from the past (6 years ago), is the above information still a good way to deal with passwords to be stored in a database? Has this been finally cracked? I am asking because one of my users' has been in a data breach that revealed their 'bcrypted' password. Does this mean that now the thieves have the password is it still safe to assume that they most likely are not going to be able to decipher the encrypted password? I do understand that theoretically one needs to go ahead and change any breached passwords, but I am asking in case the user is not changing the password. Thanks! – Alain Sep 19 '19 at 16:23
  • Personally I disagree with this. Encryption is the only way to go. Hashing makes things more complicated, and doesn't really make things that much more secure. Encryption at rest, and ssl/tls passwords in transit. Feel free to let me know why I am wrong (lol). – Technoob1984 Jun 23 '21 at 16:34
4

You simply can't.

bcrypt uses salting, of different rounds, I use 10 usually.

bcrypt.hash(req.body.password,10,function(error,response){ }

This 10 is salting random string into your password.

leopal
  • 4,711
  • 1
  • 25
  • 35
Sourabh
  • 207
  • 2
  • 2
4

To answer the original posters question.... to 'decrypt' the password, you have to do what a password cracker would do.

In other words, you'd run a program to read from a large list of potential passwords (a password dictionary) and you'd hash each one using bcrypt and the salt and complexity from the password you're trying to decipher. If you're lucky you'll find a match, but if the password is a strong one then you likely won't find a match.

Bcrypt has the added security characteristic of being a slow hash. If your password had been hashed with md5 (terrible choice) then you'd be able to check billions of passwords per second, but since it's hashed using bcrypt you will be able to check far fewer per second.

The fact that bcrypt is slow to hash and salted is what makes it a good choice for password storage even today. That being said I believe NIST recommends the PBKDF2 for password hashing.

toshiro92
  • 1,287
  • 5
  • 28
  • 42
stackhouse
  • 96
  • 5
0

Maybe you search this? For example in my case I use Symfony 4.4 (PHP). If you want to update User, you need to insert the User password encrypted and test with the current Password not encrypted to verify if it's the same User.

For example :

public function updateUser(Request $req)
      {
         $entityManager = $this->getDoctrine()->getManager();
         $repository = $entityManager->getRepository(User::class);
         $user = $repository->find($req->get(id)); // get User from your DB

         if($user == null){
            throw  $this->createNotFoundException('User doesn\'t exist!!', $user);
         }
         $password_old_encrypted = $user->getPassword();//in your DB is always encrypted.
         $passwordToUpdate = $req->get('password'); // not encrypted yet from request.

         $passwordToUpdateEncrypted = password_hash($passwordToUpdate , PASSWORD_DEFAULT);

         // VERIFY IF IT'S THE SAME PASSWORD
         $isPass = password_verify($passwordToUpdateEncrypted , $password_old_encrypted );

         if($isPass === false){ // failure
            throw  $this->createNotFoundException('Your password is not valid', null);
         }

        return $isPass; // true!! it's the same password !!!
    
      }

Community
  • 1
  • 1
  • 1
    When answering a question, talking about **what the code _does_** is important. Code-only answers can be hard to understand. – Momoro Apr 30 '20 at 21:20
-1

You can use the password_verify function with the PHP. It verifies that a password matches with the hash

password_verify ( string $password , string $hash ) : bool

more details: https://www.php.net/manual/en/function.password-verify.php

-1
import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

// Save password in hash
func HashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    return string(bytes), err
}

// check password in hash
func CheckPasswordHash(password, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

func main() {
    password := "password"
    hash, _ := HashPassword(password) // ignore error for the sake of simplicity

    fmt.Println("Password:", password)
    fmt.Println("Hash:    ", hash)

    match := CheckPasswordHash(password, hash)
    fmt.Println("Password match or not:", match) // It will return true or false
}
  • Thank you for this code snippet, which might provide some limited, immediate help. A [proper explanation](https://meta.stackexchange.com/q/114762/349538) would greatly improve its long-term value by showing why this is a good solution to the problem and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you’ve made. – helvete Mar 17 '22 at 14:12