http://www.perlmonks.org?node_id=1183260

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

Not strictly a Perl problem.

Say you have a script, on a Linux machine, which needs to communicate with a database. It needs a username and password.

Hard-coding the password into the script isn't secure, and also the password will change.

I'm sure this is a solved problem, but I can't think what the solution is. Can the script request a password from another place without a coder who is editing the script being able to view the password?
  • Comment on How can a script use a password without making the password visible?

Replies are listed 'Best First'.
Re: How can a script use a password without making the password visible?
by hippo (Bishop) on Mar 01, 2017 at 11:21 UTC
    • Environment variables
    • Per-user config files
    • Command-line switches
    • Prompt the user at run-time (ie. STDIN)

    Take your pick but bear in mind that most of these have security concerns depending on who has access to what.

    Also be aware that any coder working on a script can have it output any data from the script in any form (save to a file, email it, FTP it, whatever) so you have to trust the coder at some level.

    Addendum: Also consider the related FAQ

      Environment variables

      Insecure. See Re^2: exec, echo and pipe and below.

      Per-user config files

      With chmod 0600, ok if you can trust root and the filesystem containing the configuration file is not exposed to other machines (e.g. via NFS or Samba). Insecure if running on a shared hosting webserver, where all code runs using the same user account.

      Command-line switches

      Insecure. See Re^2: exec, echo and pipe and below.

      Prompt the user at run-time (ie. STDIN)

      Won't work in a web environment.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        ok if you can trust root

        If you can't trust root then I'd suggest that you have bigger problems than just protecting your config files.

Re: How can a script use a password without making the password visible?
by Discipulus (Canon) on Mar 01, 2017 at 11:56 UTC
    hello Cody Fendant

    I'd go with the last option by hippo above, but to be more professional you can add Term::ReadKey's ReadMode to hide what was typed.

    use Term::ReadKey; print "password please:\n"; ReadMode 'noecho'; my $password = ReadLine 0; chomp $password; ReadMode 'normal';

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: How can a script use a password without making the password visible?
by thanos1983 (Parson) on Mar 01, 2017 at 11:33 UTC

      Passwords, hashes, and salt

      Crypt::Password

      Storing Password in an easy and secure way using Perl

      I didn't bother to read the article at the third link in detail, the headings are sufficient to know that this does not answer the question. This nice article hashes passwords and adds salting. An excellent idea when working with user passwords (see Anger Management). But it absolutely does not help when your script needs to present a password to an external service, like a database. The point of hashing and salting is to make the password unrecoverable, or at least make it hard to recover it (see Rainbow Tables).

      Data::Encrypted

      This actually allows to store and recover data in an encrypted way. But this module is severely limited on its own:

      SSH1 vs. SSH2 and other public-key considerations

      Data::Encrypted utilizes the facilities made available by Crypt::RSA, and so is limited only by Crypt::RSA's ability to read and utilize various public key formats. Currently that means that only SSH version 1 keys are usable. Furthermore, keys which have been themselves encrypted via use of a 'passphrase' are currently unusable by Data::Encrypted -- future versions may overcome this limitation.

      Yes, it does encrypt any data you wish, like a database username and password. But: It relies on two files on the same machine (the private and public RSA key files), it uses old crypto algorithms (RSA, SSH1), and it can't even handle private keys with passphrases.

      What does that gain? An attacker needs to copy not only the encrypted configuration file, but also the private key file (~/.ssh/id_rsa.pub). The public key can easily be derived from the private key file. And as Data::Encrypted does not support private keys that need a passphrase, the attacker does not have to guess anything. Having the private key file is sufficient to decrypt the configuration file. So, instead of copying one file with mode 0600, the attacker has to copy two files with mode 0600. That's a 100% security improvement. Bonus points for spotting any irony in the previous sentence.

      Now, how much work is required to decrypt the configuration file? Let's assume the attacker was able not only to copy the private key file and the encrypted configuration file, but also the script. And let's assume the script followed the "Real Life Examples" from Data::Encrypted. Then the script will contain something like this:

      use DBI; use Data::Encrypted; my $encryptor = Data::Encrypted->new(FILE => 'db.conf'); my $dbh = DBI->connect( 'dbi:mysql:mydatabase', $encryptor->encrypted('user name'), $encryptor->encrypted('db password'), { RaiseError => 1, AutoCommit => 1} );

      Let's modify that, just a little bit:

      use DBI; use Data::Encrypted; my $encryptor = Data::Encrypted->new(FILE => 'db.conf'); my $dbh = die join(' :: ', # <--- this is the only line modified 'dbi:mysql:mydatabase', $encryptor->encrypted('user name'), $encryptor->encrypted('db password'), { RaiseError => 1, AutoCommit => 1} );

      And then run it:

      dbi:mysql:mydatabase :: clueless :: t0ps3cr3t :: HASH(0x1158f30) at pe +rfectlysecure.pl line 10.

      Did you notice how much I had to think about crypto for that attack? Exactly zero. I didn't break RSA at all.

      Now, let's imagine what would happen if Data::Encrypted would actually use current crypto algorithms, the hardest ones you could imagine, and it would support using a passphrase to unlock the private key. What would that gain?

      Better crypto does not help at all, because the data is available in clear in the script. Replacing DBI->connect(...) with die join(' :: ',...) is successful even with the best crypto available on the planet.

      Using a passphrase would annoy the user, because (s)he had to type in the passphrase every time (s)he wants to use the script. That won't work at all in a server context, by the way. So, what does a lazy user do? Exactly, remove that annoying passphrase or change it to something trivial like 12345, qwerty, or asdf. But the user does not have to enter the database password.

      So, is the DB password well-protected? Nope, nothing stops the user from running a modified script that reveals database user and database password. I've shown above how easy that is.

      Related: Re^2: Where should I have configuration information in a file or database, Re^2: best way to store login information for a perl script?, Re: Protection for Perl Scripts, Re^3: Code hiding in Perl

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: How can a script use a password without making the password visible?
by marto (Cardinal) on Mar 01, 2017 at 11:59 UTC

      Hi marto,

      Are you aware of anything similar for non-Oracle DBs?

      Thanks!


      The way forward always starts with a minimal test.

        IIRC MySQL has a plugin to talk to the Oracle system linked above. Besides casual/hobby use I don't get a chance to dabble much with other databases. Our client work is almost totally Oracle based, with some SQLite stuff I've had to build, so unfortunately I don't have a sufficient exposure to other DBs, I'm sure the terminology to search for will be similar, minus the Oracle sales patter.

Re: How can a script use a password without making the password visible?
by soonix (Canon) on Mar 01, 2017 at 12:51 UTC
    File::KeePass looks promising. However, I myself haven't yet tried it.
Re: How can a script use a password without making the password visible?
by shmem (Chancellor) on Mar 01, 2017 at 13:45 UTC
    I'm sure this is a solved problem, but I can't think what the solution is.

    Maybe it is, but I'm not aware of a reusable implementation of that solution...

    Can the script request a password from another place without a coder who is editing the script being able to view the password?

    In the near future I will have to implement something like that, my ideas so far:

    • script opens a secure connection to a secure password server (protocol irrelevant, ssl/http, ssl/soap, ssh, whatever) and keeps the connection open waiting for a response
    • server process opens a ssh connection to the machine of the supplicant, with pubkey authentication
    • server checks client process, uid, script checksum, open file descriptors etc to be sure this client is the right one to be answered - and that nobody tampers with or traces it
    • if checks are positive (checks are done against database entries on the server) the server emits the password to the client and closes the connection

    That way it is the password server's duty to make sure it deals out credentials only to authorized clients. Every change to the client script, i.e. its checksum must be propagated to the server. While it is not impossible for a third party to get the password on the client's machine (which is always compromised in the view of the password server), it is much more difficult than reading a configuration file or the scripts source code.

    Security stands and falls with the ability of the granting part to verify the integrity of the requesting part, which might be impossible. But any attempt is better than nothing. There's no limit to "paranoia" ;-)

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re: How can a script use a password without making the password visible?
by bart (Canon) on Mar 01, 2017 at 18:04 UTC
    A coder with access to the script can do anything with the password, including printing it to the console. They can read the entire database too, or wipe tables. So, if you cannot trust your coders, you have a huge problem.

    If your real fear is that outsiders accidentally get read access to the code, and can see the password, then I would recommend restricting access using the password to localhost — or for an IP address on your local network. MySQL (and, thus, MariaDB) allow you to define a login (user/password combination) that doesn't work remotely, and I'm quite sure PostGres does too.

    In fact, I often use no password for a restricted user, in applications on a website. You cannot login without password in PHPMyAdmin, and other similar database CRUD tools (which actually work on localhost/intranet). So, having no password actually protects you from such hackers.

Re: How can a script use a password without making the password visible?
by Anonymous Monk on Mar 01, 2017 at 16:02 UTC

    I would like to add Config::Identity to the mix. It is a front-end for GPG. Both GPG and Config::Identity take a bit of setting up, and in particular you may want to point environment variable CI_GPG to your gpg2 executable if you are using a pinentry back end, since otherwise Config::Identity will prefer gpg.

Re: How can a script use a password without making the password visible?
by ikegami (Patriarch) on Mar 01, 2017 at 17:45 UTC

    without a coder who is editing the script being able to view the password?

    No. The coder/script will obviously see what it will pass to the service/database.

      That's not necessarily true. The coder can run the script in the dev environment, so they can see the dev database password. But the production db passwords remains hidden to them.

      Moreover, the coder can't change the code to show the password. Such a change wouldn't pass the code review.

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        Note that the OP wants to hide the password *from the script*. Even in that scenario, the script will still see the password.

        But yeah, it's a good point that access to both the production password and the production script should be controlled.

Re: How can a script use a password without making the password visible?
by holyghost (Beadle) on Mar 02, 2017 at 04:12 UTC
    If you enter the password on the command line, you can set the terminal option NOECHO so you don't see what you type. You can also delete the memory where the password was stored.
Re: How can a script use a password without making the password visible?
by CountZero (Bishop) on Mar 02, 2017 at 07:34 UTC
    If the coder has enough access rights to edit the script, he has access to the credentials needed to access the external pasword storage and/or he can edit the script to expose the pasword.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics
      If the coder has enough access rights to edit the script, he has access to the credentials needed to access the external pasword storage and/or he can edit the script to expose the pasword.

      Write access is not needed, read access is sufficient: Just copy the script, modify the copy, run the copy.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Reading and local editing is not enough. He must be able to write the edited script on the system where he found it in order to have it run there.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics