Benoit Vanalderweireldt
Benoit Vanalderweireldt

Reputation: 2989

How could I hide/protect password from a Perl script

I'm writing a Perl script that needs to connect to a SMTP server in order to send a mail, but I really don't like this kind of things :

my $pass = '123456';

And I found Data::Encrypted, that should allow the user to prompt it the first time and then store it encrypted.

use Data::Encrypted file => ".passwd", qw(encrypted);
my $password = encrypted('password');

But I cannot make it work, it makes a running time error :

Bad key file format at /Library/Perl/5.12/Data/Encrypted.pm line 78

Is anybody having the same issue, or know another way to hide/protect password?

Upvotes: 9

Views: 14990

Answers (4)

Dinesh Ravi
Dinesh Ravi

Reputation: 1225

here is the full blown code which utilizes part of code mentioned above and part taken from the Perlmonk. The script first asks the username and password from the user, encrypts and stores it in the .crypt file. Then reads from it, decrypts and shows the original text. On second time it will use the existing user credentials.

use Crypt::Rijndael;
use IO::Prompter;
use Crypt::CBC;
#keys
my $key = "a" x 32;
my $cipher = Crypt::CBC->new( -cipher => 'Rijndael', -key => $key );
my @plaintext;
my @ciphertext;
#keys

#filefield
#password file name
#my $file_name = ".crypt";
my $file_name = ".crypt";
#File Handler
my $file;


#If we cannot open the password file we initiate a new one
unless ( open ( $file, '<:encoding(UTF-8)', $file_name) ) { #<:encoding(UTF-8)
#Create a new file in write mode
    open ( $file, '>', $file_name);
    $plaintext[0]= prompt "Username:";
    $plaintext[1]= prompt "Password:", -echo => '';
    print "#################################################################################\n";
    print "# User credentials will be encrypted and stored in .crypt file and same is      #\n"; 
    print "# reused next time.  If you need to add new user credentials delete the .crypt  #\n";
    print "# file and re run the same script.                                              #\n";
    print "#################################################################################\n";
    $plaintext[0]=~ s/^\s*(.*?)\s*$/$1/;
    $plaintext[1]=~ s/^\s*(.*?)\s*$/$1/;


    while($plaintext[0] =~ /^\s*$/){
    $plaintext[0]= prompt "Username is mandatory:";
    $plaintext[0]=~ s/^\s*(.*?)\s*$/$1/;
    }
    while($plaintext[1] =~ /^\s*$/){
    $plaintext[1]= prompt "Password is mandatory:";
    $plaintext[1]=~ s/^\s*(.*?)\s*$/$1/;
    }


    $ciphertext[0] = $cipher->encrypt($plaintext[0]);
    $ciphertext[1] = $cipher->encrypt($plaintext[1]);

    #we save the password in a file
    print $file $ciphertext[0];

    #print $file "\n";
    #we save the password in a file
    print $file $ciphertext[1];
     #we close the file ( Writing mode )
    close $file;

    #Reopen the file in reading mode
    open ( $file, '<', $file_name)
 }


 my @holder;
 my $content;
if (open( $file, '<', $file_name)) {
  #chomp(@holder = <$file>);
 local $/;
    $content = <$file>;

} else {
  warn "Could not open file '$filename' $!";
}
@holder = split(/(?=Salted__)/, $content);
  print "Encrypted username:",$holder[0];
  print "\n";
  print "Encrypted password:",$holder[1],"\n";

 #Loading the password en decrypt it
$plaintext[0] = $cipher->decrypt( $holder[0] );
$plaintext[1] = $cipher->decrypt( $holder[1] );

print "\n\n";

print 'Username is:',"$plaintext[0]\n";
print 'Password is:',"$plaintext[1]\n";
#Close the file
close $file;

#filefield

Upvotes: 0

Hynek -Pichi- Vychodil
Hynek -Pichi- Vychodil

Reputation: 26121

When you are dealing with a script which is sending plain text password to a service without any user interaction your are already doomed. Any solution you will come with will be just security by obscurity. You can come with solution as zostay did. But it is equivalent of buying most advanced vault but letting the key under the mat and sticking paper with the text: "Check the mat for the key!" to the front door. Look, I will just copy the script, grep for the password. Then I will found line like my $password = $crypto->decrypt($password); and place warn $password; just at line below and run the script. That's it. I don't care what algorithm you use, I don't care where and how you store the password. You can make me it harder, but my effort to crack will be always a several order of magnitude less than your effort to make it hard. Your script is the key. Look at all this movie industry. They spent billions to come with the bunch of silly crap. They ended up with special HW, even cable has its own key. Hilarious! It is harassing only fair users.

Place plain password in the script if you don't want look silly. If you want go with security by obscurity then don't name variables with sensible names, don't use any standard module (look, method decrypt is clue!) and don't waste your time with sophistication. I will not look how you store or encrypt the password, I will look where you will have to use it and hook there. It is much easier and much harder to hide.

Upvotes: 3

Benoit Vanalderweireldt
Benoit Vanalderweireldt

Reputation: 2989

Thanks ! Here is my final solution :

sub smtp_passwd(){
    #The secret pass phrase
    my $app_secret = 'd.<,3eJ8sh[(#@1jHD829J,Z!*dGsH34';

    #password file name
    my $passwd_file_name = ".passwd";

    # Setup the encryption system
    my $crypto = Crypt::Rijndael->new( $app_secret, Crypt::Rijndael::MODE_CBC() );

    #File Handler
    my $passwd_file;

    #If we cannot open the password file we initiate a new one
    unless ( open ( $passwd_file, '<', $passwd_file_name) ) {

        #Create a new file in write mode
        open ( $passwd_file, '>', $passwd_file_name);

        # Ask the user to enter the password the first time
        my $password = prompt "password: ", -echo => ''; # from IO::Prompter

        #Password must be multiple of 16 (we deliberately chose 16)
        my $pass_length = 16;

        #If password is to short we complete with blank
        $password = $password." "x ($pass_length - length ( $password ) ) if ( length ( $password ) < $pass_length );

        #If password is to long we cut it
        $password = substr ( $password, 0, $pass_length ) if ( length ( $password ) > $pass_length );

        #Encryption of the password
        my $enc_password = $crypto->encrypt($password);

        #we save the password in a file
        print $passwd_file $enc_password;

        #we close the file ( Writing mode )
        close $passwd_file;

        #Reopen the file in reading mode
        open ( $passwd_file, '<', $passwd_file_name)
    }

    #Loading the password en decrypt it
    my $password = $crypto->decrypt( <$passwd_file> );

    #Close the file
    close $passwd_file;

    #Return the password ( Here the password is not protected )
    return $password;
}

Upvotes: 3

zostay
zostay

Reputation: 3995

The Data::Encrypted module was last released in 2001. I'd say that's a good sign not to use it.

Normally, I'd say storing passwords at all is a bad idea even encrypted. However, if you must store a password for use contacting another system, encrypting it is the way to go. The way I would do it is something like this:

# Rijndael is also known as AES, which is the encryption standard used by the NSA
use Crypt::Rijndael;
use IO::Prompter;

# This secret is exactly 32 bytes long, you could prompt for this as a
# passphrase or something and pad it with spaces or whatever you need
my $app_secret = 'this_is_the_key_the_app_uses....';

# Setup the encryption system
my $crypto = Crypt::Rijndael->new( $app_secret, Crypt::Rijndael::MODE_CBC() );

# Ask the user to enter the password the first time
my $password = prompt "password: ", -echo => ''; # from IO::Prompter

# Encrypt the password. You can save this off into a file however you need to
my $enc_password = $crypto->encrypt($password);

# Later load it from the file and decrypt it:
my $password = $crypto->decrypt($password);

For more information see Crypt::Rijndael and IO::Prompter.

Upvotes: 12

Related Questions