Suppose a Perl program, whether it be a fancy GUI application or a duct-tape style script, needs a password. How secure the password needs to be really depends on the application and the specific usage, but in general it ought to be stored "elsewhere", not in the script itself. It might prompt the interactive user, or read it from another file or media or system registry.

I'm thinking this can be made into a module. It should be simple enough to use from duct-tape scripts that run non-interactively, say $x= get_password("identifier") and some other utility program or config file can map what "identifier" does, whether store it directly, store it in protected media or non-file storage of some kind, prompt the user via low-Tempest GUI with integrated keyboard-logging spoofing, etc.

This decouples the script's need for a password and the security policy implementation.

Is anything like that around already?

Any thoughts on the concept or design?

—John

Replies are listed 'Best First'.
Re: How about a module for "passwords"?
by BrowserUk (Patriarch) on Jan 25, 2003 at 00:02 UTC

    Generally, I would think it better to use the OS' protection mechanisms for this sort of thing.

    Set the file access right for the script such that only members of a given group can execute the program and make authorised users a member of that group. That way authorised users only need to remember their standard login passwerds rather than 1 per task.


    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

      That makes the entire script sensitive. You have to remember to "sanitise" it before sharing the code. And what about backups or version control copies? I think it's useful to have the sensitive information in a different place, even if OS file protection is protection enough.
Re: How about a module for "passwords"?
by zengargoyle (Deacon) on Jan 25, 2003 at 11:22 UTC

    i have that in ugly code that really needs cleaning. it seemed quite useful at the time. the client uses Kerberos to request passwords from the server. all the passwords are returned in an Tie::Encrypted::Hash (or maybe it's Tie::Hash::Encrypted ;) along with a key. that part is slightly mushed together with a (i beg forgiveness for the following...) Passport (cough) like part that will store the encrypted hash somewhere locally.

    the end effect is roughly thus:

    • if you have Kerberos, a valid ticket, and the server is working... you can get your passwords without having to type anything.
    • otherwise, you'll be prompted for a key and thereafter you'll be asked for a password only the first time it's needed, it will live in the encrypted hash and saved on DESTROY. the next time around you just have to type your key.

    it needs: error checking, logging, all the good stuff. but it did work quite well. the application was webified removing the need for the user to keep track of the passwords.

    if there's any interest i can put it up somewhere after removing incriminating evidence.

      if there's any interest... Yes, I'd love to hear more. I never thought about Kerberos for anything other than OS logins! I suppose it has a general interface to hang onto anything you want, though, right?

      So if I understand, Kerberos is used to say "I already validated that Foo is logged in, so I'll let him access this stuff that only Foo should know." Otherwise, the user has to type the passphrase. After that, it behaves identically, using the key it obtained on a encrypted hash. Is that right?

      —John

        i commented out the Kerberos part of this, but you got the basic idea. if you have a valid ticket you can connect to the server and the server can check you against its list of valid users. the server then sends the encrypted hash (with the __password and __cipher preset) back to the client to use as desired.

        the Kerberos parts are much uglier. i'll need to poke around them some more before posting.

        package Keystore; use strict; use warnings; use Tie::EncryptedHash; use Term::ReadKey; use Data::Dumper; my $default_keyfile = "$ENV{HOME}/.Q/keystore"; my $default_secret = 'notverysecret'; my $default_cipher = 'Blowfish'; sub _get_secret { my $prompt = shift; my $secret; print STDERR "$prompt: "; ReadMode 'noecho'; $secret = ReadLine 0; chomp $secret; ReadMode 'normal'; print STDERR "\n"; return $secret; } sub pw { my ($self,$password,$newval) = @_; my $keystore = $self->{keystore}; return undef unless (defined($password) and length $password); my $encpass = "_$password"; # Tie::Encrypted uses _ for the +encrypted keys if (exists($keystore->{$encpass})) { return $keystore->{$encpass} || ''; } elsif (defined($newval) and length $newval) { $keystore->{$encpass} = $newval; return $keystore->{$encpass} || ''; } else { $keystore->{$encpass} = _get_secret($password); return $keystore->{$encpass} || ''; } return undef; } sub new { my ($c,%param) = @_; =begin comment # this was the Kerberos part, instead of reading from a file i +t # would connect to a Kerberos server (using your credentials). # if all went well it would recieve the encrypted hash from th +e # server and would leave the keyfile 'undef' so it wouldn't be # saved on DESTROY. if the server chat failed it failed to the # file based method below. my $kks = KKS::keystore; if (ref $kks) { my $self = { keystore => $kks, keyfile => undef }; return bless $self, $c; } =cut $param{keyfile} ||= $default_keyfile; if (-f $param{keyfile} && !-r _) { $param{secret} = $default_s +ecret; } $param{secret} ||= _get_secret('secret'); my (%s,%sold); if (-f _ && -r _) { open(F,"<$param{keyfile}") or warn "open\n"; my ($cfgstr,$passwords); {local $/ = undef; $cfgstr = <F>; } eval "$cfgstr"; # ewwww!! would not do this this +way now... %sold = (%$passwords); } tie %s, 'Tie::EncryptedHash'; %s = (%sold); $s{__password} = $param{secret}; $s{__cipher} = $param{cipher} || $default_cipher; my $self = { keystore => \%s, keyfile => $param{keyfile} }; return bless $self, $c; } sub DESTROY { my ($s) = @_; my ($k,$f) = ($s->{keystore}, $s->{keyfile}); { local $^W = 0; no warnings; delete $k->{__password}; } return unless defined $f; open(F,">$f") or return; print F Data::Dumper->Dump([$k],['passwords']); close(F); } 1; =head1 NAME Keystore - Perl extension for a password keystore. =head1 SYNOPSIS use Keystore; $ks = new Keystore; $passwd = $ks->pw('cisco_console'); $passwd2 = $ks->pw('cisco_enable'); # do something query_rtr('rtr-foo',$passwd,$passwd2); # clean up if you're paranoid $passwd = $passwd2 = undef; =head1 DESCRIPTION Try it: $ mkdir $HOME/.Q # type the following on a single line so you can up-arrow and do it a +gain. $ perl -e 'use Keystore;$ks=new Keystore; \ for(qw( testpass anotherpass foopass)) { \ print $ks->pw($_),"\n";}' # type a secret, it will be used to encrypt your passwords. # since it doesn't know these passwords, it will ask. secret: testpass: foo anotherpass: lala foopass: zig # do it again, type the same secret as before. # it knows them now, doesn't need to ask. secret: foo lala zig $ cat $HOME/.Q/keystore $passwords = { '_testpass' => 'Blowfish rL0Y20zC+Fzt72VPzMSk2A 52616e646f6d4956fde +2cd4cc69fb 4ab6888532e6b2aac86', '_foopass' => 'Blowfish I4uNgfsODwP+rVYTtSNLdA 52616e646f6d49567073 +d91467ce68 e7d45d91ad73b75cc7', '_anotherpass' => 'Blowfish LjgXKT/CddvudL1xzm6wVg 52616e646f6d4956 +2070a13f37 a38f8dcd55403f697a6661' }; To delete a password, just delete the line. To completely disable the + encrypted saving of passwords: chmod -rw $HOME/.Q/keystore If it can't read the + file it will prompt for passwords when needed (but still only once per sess +ion). If it can't write the file they won't get saved. =head2 EXPORT None by default. =head1 AUTHOR =head1 SEE ALSO L<Tie::EncryptedHash>. =cut
Re: How about a module for "passwords"?
by Ryszard (Priest) on Jan 25, 2003 at 17:29 UTC
    Where i work we have an environemnt in which many different scripts must have access to different hosts, databases etc etc. We use this method:
    package Attr; use strict; use vars qw(%struct); %struct = ( infomgr => { sid => 'xxxxxx', username => 'xxxxxx', passwd => 'xxxxxx', }, )

    This way we can have multiple hosts, databases (in fact any arbitrary data.)

    To reference the data in our scripts:

    use Attr (); # Application wide configuration use vars qw(%struct); # Grab the datastructure from the config fil +e *struct = \%Attr::struct; # Alias the variable out .. .. print $self->{config}{infomgr}{sid} # for want of a better example
    We also have the OS accounts locked down tightly as well as permissions on the file. This works quite well for us as we've got a single point for all our configuration data.

      It sounds like your package Attr should be exporting the struct. Then the script can use Attr qw/%struct/; instead of the two additional lines you have there.

      I see the point of the system, though. Multiple scripts can use the same configuration data, and that data can be protected and treated differently than the scripts.

      —John

        yeah, good call, there was some reason i didnt do it in the 1st place, probably laziness.. ;-)
Re: How about a module for "passwords"?
by ibanix (Hermit) on Jan 25, 2003 at 10:03 UTC
    my $password = $ARGV[0]; has been working fine in my password-sensitive scripts for ages.

    Cheers,
    ibanix

    $ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
      my $password = $ARGV[0]; has been working fine in my password-sensitive scripts for ages.

      The smiley appears to be missing from the end of that line :-)

      For those who are thinking of following this advice, be aware that having your script accept the password as a command line argument means that the plaintext password will be visible to anyone looking over your shoulder, anyone who can run the ps command, anyone who can read your shell's command history file etc, etc, etc.

      I just had to add:

      protected media or non-file storage of some kind, prompt the user via low-Tempest GUI with integrated keyboard-logging spoofing

      If you need this level of security or have assests worthy of this type of costly attack, you should be investing in anti-surveilence equipment. Fit the security to the risk.

      ibanix

      $ echo '$0 & $0 &' > foo; chmod a+x foo; foo;
        I just wanted to show an extreme "serious security" example.

        And that's what NAI's PGP for Windows does, BTW.

Re: How about a module for "passwords"?
by traveler (Parson) on Jan 25, 2003 at 22:04 UTC
    This sounds interesting, but may need to be more complex. Consider:
    • Can one ask for different passwords in a script (e.g the read passwd then the write passwd)? If so you need to specify which one in get_passwd
    • How does the provider know/authenticate who is asking? If I ask a file for a passwd, to whom does it give it up? Are OS read perms all we use?
    • Let's say the passwd is on a USB dongle. I need to provide a string to the dongle to check it. How do I do that?
    Just a few things to think about.

    --traveler

      Just a few things to think about.

      Thanks.

      Can one ask for different passwords in a script?

      Yes, the idea all along was to have an identifier to correspond to the password of interest.

      I need to provide a string to the dongle to check it. How do I do that?

      The rough inkling I have at this point is that the identifiers will map to information specifying which concrete password module to use, and any other parameters it needs for that; e.g. the corresponding identifiers used by that particular system, such as database table row, registry key name, dongle slot.

      How does the provider know/authenticate who is asking? If I ask a file for a passwd, to whom does it give it up? Are OS read perms all we use?

      Well, it depends. Different concrete classes could do totally different things. The real value is to have a common abstract interface and central module to dispatch to the correct concrete module for that password. A simple nieve file-based table would rely on OS permissions, secure file system, or removable media to give it any real security. An implementation module could certainly prompt for a password or key itself.

      After reading the replies on this thread, I do see that the password getter object needs to have a state that can hold information for the concrete modules, so that (for example) the script's user can be authenticated once and then the key is remembered for the life of the script.

      —John