http://qs1969.pair.com?node_id=928590

nimdokk has asked for the wisdom of the Perl Monks concerning the following question:

I've done a bit of searching here on this topic, but either I'm not asking myself the right question or I'm missing something. I have a script that accesses a Windows app via COM API. The app requires a connection that includes a user name/password to authenticate so you can access and do what you need to do. Currently, the user ID/password are hard coded into the script so anyone on my team who uses the script does the work as that user instead of their own account. I've initiated a request to our vendor to address this so that we can use something like the "currently logged on user" for our authentication via the COM API (this is functionality availble in the GUI front-end but not apparently in the COM API objects we can access). Not sure when this will get done. In the meantime, I'd revising things so that when a user runs the script, it will prompt them for their user ID and then password which would then be passed in to authenticate the connection. My question: is there a good/recommended way to prompt a user for a password and then hash it or do something so that the password is not kept in clear text but can be passed for authenticating the connection to the app. The audience on this functionality is extremely limited to myself and my two co-workers (no one else would be able to run and administer the application using these scripts). I'm really not sure where to begin beyond setting the script up to prompt for user ID/password. Here is what I have at the moment:
use strict; use warnings; use Win32::OLE; use Data::Dumper; ## specify EFT connection information. If the eftadmin password change +s, it needs to be changed here as well. our $domain='OurDomain\\'; our $pass; our $user; CONNECTION: { print "Account ID? "; chomp (my $s_id=<STDIN>); $user=$domain . $s_id; print "Using $user to authenticate.\n"; print "Password? "; chomp (my $pass=<STDIN>); print "Password: $pass\n"; }
One thought I had would be to make the connection right away and then change the value of $pass to some garbage (or set it to undef or something like that so it's no longer valid - it would only be needed once to make the connection - the code to make the connection has not been included in this sample). If there's something I can look at to figure out my issue, please let me know (I'm not looking for a specific answer - just some place I can go to find an answer - or suggestions). If possible, I'd like to avoid having to load additional modules (unless that is the best answer). Thanks in advance

Replies are listed 'Best First'.
Re: Masking Windows Passwords
by VinsWorldcom (Prior) on Sep 29, 2011 at 15:09 UTC

    I'm not sure I understand the question when read with the title. If you're looking to mask the password when entered on the command line, use Term::Readkey.

    Your script with the Term::Readkey stuff inserted - I check to see if you have it installed first. Not sure if it is a core module. If not, then just install it, not a big deal.

    use strict; use warnings; use Win32::OLE; use Data::Dumper; # use Term::ReadKey my $HAVE_Term_ReadKey = eval { require Term::ReadKey; Term::ReadKey->import; 1 }; ## specify EFT connection information. If the eftadmin password change +s, it needs to be changed here as well. our $domain='OurDomain\\'; our $pass; our $user; CONNECTION: { print "Account ID? "; chomp (my $s_id=<STDIN>); $user=$domain . $s_id; print " Using $user to authenticate.\n"; print "Password? "; if ($HAVE_Term_ReadKey) { ReadMode(2) } chomp (my $pass=<STDIN>); if ($HAVE_Term_ReadKey) { ReadMode(0) } print "\n Password: $pass\n"; print " CONNECTING TO APP ...\n"; print " CONNECTED TO APP ...\n"; $pass = undef; print "Password: $pass\n" }

    The script outputs:

    VinsWorldcom@C:\Users\VinsWorldcom\tmp> test Account ID? vinsworldcom Using OurDomain\vinsworldcom to authenticate. Password? Password: My_Password CONNECTING TO APP ... CONNECTED TO APP ... Use of uninitialized value $pass in concatenation (.) or string at C:\ +Users\VinsWorldcom\tmp\test.pl line 34, <STDIN> line 2. Password: VinsWorldcom@C:\Users\VinsWorldcom\tmp>

    Notice the "Password?" prompt has no text next to it as the ReadMode(2) call turns off echo so you don't see the user typing their password. It does get saved to $pass however. But also notice the "Use of uninitialized ..." error after "CONNECT..." because we set $pass to 'undef' and then print it - just so you see it has been "erased" after using it to authenticate.

    Are you concerned that people will be snooping the memory of the computer you are running the script on while you're running the script - and thus need the obfuscation of the $pass variable immediately?

Re: Masking Windows Passwords
by zentara (Archbishop) on Sep 29, 2011 at 18:04 UTC
    If you can use Tk , this will do it in a GUI. It pops the GUI, gets the user and pass, then undefines the password after it is used to authorize.
    #!/usr/bin/perl use warnings; use strict; use Tk; my ($user, $password); my $mw = MainWindow->new; $mw->title("Password Entry"); $mw->withdraw; # let size be determined before centering $mw->fontCreate('big', -weight=>'bold', -size=> 14 ); my $buttom = $mw -> Button(-text => "SUBMIT", -font => 'big', -bg => 'white', -command => \&somesub) ->pack(-side => 'bottom',-anchor => 'center'); my $label = $mw -> Label(-text => "Enter User and Password", -font => 'big', -bg => 'white', )->pack(-side => 'top', -anchor => 'center'); my $label1 = $mw -> Label(-justify => 'left', -text => "User: ", -font => 'big', -bg => 'white', ) ->pack(-side => 'left', -expand => 1); my $entry = $mw -> Entry(-selectborderwidth => 10, -font => 'big', -bg => 'white', )->pack(-side => 'left', -expand => 1); ############################################################### my $label2 = $mw -> Label(-justify => 'left', -text => "Password: ", -font => 'big', -bg => 'white',) ->pack(-side => 'left', -anchor => 'n'); my $entry1 = $mw -> Entry(-selectborderwidth => 10, -show => "*", -font => 'big', -bg => 'white', )->pack(-side => 'top', -anchor => 'w'); ############################################################## # an enter in the password entry will submit $entry1->bind('<Return>',[\&somesub]); $entry->focus; center($mw); MainLoop; sub somesub { $user = $entry->get; chomp $user; $password = $entry1->get; chomp $password; #print "$user $password"; $mw -> destroy; } sub center { my $win = shift; $win->withdraw; # Hide the window while we move it about $win->update; # Make sure width and height are current # Center window my $xpos = int(($win->screenwidth - $win->width ) / 2 ); my $ypos = int(($win->screenheight - $win->height ) / 2 ); $win->geometry("+$xpos+$ypos"); $win->deiconify; # Show the window again } # now Tk is finished, you can send the $user and $password to your ser +ver # your script continues here, the prints are just for demo purposes he +re print "Hit Enter to continue and send info to server\n"; <>; # send to server or wherever print "user->$user password->$password\n"; # now undefine password $password = undef; print "Hit Enter to exit, password is undefined\n"; <>;

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Masking Windows Passwords
by nimdokk (Vicar) on Sep 29, 2011 at 15:46 UTC
    I think Term::ReadKey is exactly what I need. It looks like we've already got it so I'll check it out. As for the title, I couldn't think of a good, short description of the issue. Thanks.
Re: Masking Windows Passwords
by sundialsvc4 (Abbot) on Sep 29, 2011 at 18:02 UTC

    What you’s really like to do is to have access to the COM interface controlled such that no one or no application can gain access to it at all without being authorized through the existing Windows (OpenDirectory / LDAP / etc.) authentication mechanisms.

    The request itself might be accompanied by some random identification-token which is simply a calling-card.   Some COM interfaces oblige you to send a hash of a userID/password combination (structured however the vendor requires) across the wire:   the receiving computer knows what the correct hash-value should be, but no one who’s looking at the transmission has any idea.

    Microsoft has some trustworthy interface abilities already built-in to their IIS server which do give you a way to find out about the user without having to ask him, and these can also be applied to the case of remote interfaces if the remote in question is intra-net.

      As mentioned, I have but a flea in the vendor's ear to address this. I've also found that via the COM objects they have available, we cannot authenticate with a domain account (even though we can use the same domain account via the GUI) even with hardcoded user name/password. This is not a Perl problem, but missing functionality (especially if they recommend not exposing passwords when using their COM API.