简体   繁体   中英

Storing Passwords in reversible form

I have a PHP app that needs to run bash scripts, and provide a username & password (for remote systems). I need to store these credentials somewhere that is accessible by my PHP (web) app. The logical place is the database (currently MySQL, but will be agnostic). The problem with the "standard" way of hashing and storing the credentials, is that it is not reversible. I have to be able to get the credentials out as unencrypted clear text, to be able to insert the data into bash scripts.

Does anyone have any suggestions for a secure way to go about this ?

I thought maybe PKI'ing the credentials, and storing the result in the DB. Then use the private key to unencrypt (PHP can do that). Store the scripts to do this outside the web root.

Any thoughts much appreciated.

First, to state the (hopefully) obvious, if you can in any way at all avoid storing usernames and passwords do so; it's a big responsibility and if your credential store is breached it may provide access to many other places for the same users (due to password sharing).

Second, if you must store credentials prefer rather to stored passwords using a non-reversible, salted cryptographic hash, so if you data is compromised the passwords cannot easily be reverse-engineered and there's no need to store a decryption key at all.

If you must store decryptable credentials:

  1. Choose a good encryption algorithm - AES-256, 3DES (dated), or a public key cipher (though I think that's unnecessary for this use). Use cryptographic software from a reputable trustworthy source - DO NOT ATTEMPT TO ROLL YOUR OWN, YOU WILL LIKELY GET IT WRONG.
  2. Use a secure random generator to generate your keys. Weak randomness is the number one cause of encryption related security failures, not cipher algorithms.
  3. Store the encryption/decryption key(s) separately from your database, in an O/S secured file, accessible only to your applications runtime profile. That way, if your DB is breached (eg through SQL injection) your key is not automatically vulnerable, since that would require access to to the HDD in general. If your O/S supports file encryption tied to a profile, use it - it can only help and it's generally transparent (eg NTFS encryption).
  4. If practical, store the keys themselves encrypted with a primary password. This usually means your app. will need that password keyed in at startup - it does no good to supply it in a parameter from a script since if your HDD is breached you must assume that both the key file and the script can be viewed.
  5. For each credential set, store a salt (unencrypted) along with the encrypted data; this is used to "prime" the encryption cipher such that two identical passwords do not produce the same cipher text - since that gives away that the passwords are the same.
  6. If the username is not necessary to locate the account record (which in your case it is not), encrypt both the username and password. If you encrypt both, encrypt them as one encryption run, eg

    userAndPass=(user+":"+pass);
    encryptInit();
    encrypt(salt);
    encrypt(userAndPass);
    cipherText=encryptFinal();

    and store the singular blob, so that there is less occurrence of short cipher texts, which are easier to break, and the username further salts the password.

PS: I don't program in PHP so cannot comment on suitable crypto s/w in that environment.

You'll need to look into good 2 way cryptographic methods, and my general rule of thumb is:

If you implement your own cryptographic code you will fail.

So, find a good implementation that is well verified, and utilize that.

There is probably some good info here:

http://phpsec.org/library/

Check this library: PECL gnupg it provides you methods to interact with gnupg. You can easily encrypt and decrypt data, using safe public-key cryptographic algorithms.

I would suggest you not store the passwords, but use passwordless ssh connection from the host to the remote system by generating a ssh key and storing your public key in the remote system's authorized_keys file. Then you would only need to establish connectivity during configuration. Admittedly not quite answering your question, but storing passwords in a reversible form is a slippery slope to a security breach imho, although I am sure smarter brains than mine can make it safe.

One easy way to get started is to use mysql's ENCODE() and DECODE() functions. I don't know what algorithm is used underneath, but it's easy enough to use:

INSERT INTO tbl_passwords SET encoded_pw = ENCODE('r00t', 'my-salt-string');

and

SELECT DECODE(encoded_pw, 'my-salt-string') FROM tbl_passwords;

For PHP, it is important to note that AES encryption is implemented via MCRYPT_RIJNDAEL functions. Don't go paying for a non-open implementation when PHP has them available.

See the PHP page discussing available ciphers for more information.

If you go the PKI, and I would, make sure you safe guard your private keys! The strong encryption provided by PKI is only as secure as your keys.

I think you're on target. Look at GPG for a good, open encryption library

It looks like you pretty much have two methods of doing this:

1) Like you suggested use an encryption algorithm or algorithms which can then be decrypted and used for authentication in your scripts. You can use the MCrypt library in PHP to accomplish this.

2) Depending on the required level of security and your script's level of vulnerability, you could use a secure hash, key, or some other hard to guess unique identifier that you can use to hijack each user's account within the confines of the script.

As many stated you scenario requires that you encrypt username and password. I would recommend that you check out the mcrypt extension of php for encryption/decryption.

I think I am going to investigate compiling a PHP script with the credentials embedded, on the fly, from the web app.

I would ask for the credentials (for a given use), then create and compile a new PHP script, for this use only. That way, the script will only do what I need, and should not be "readable". I think this sounds like the safest way to do this.

Will try using Roadsend. http://www.roadsend.com/

Just to follow up on the suggestion to use MySQL encode and decode functions, the manual is vague on just how these work:

The strength of the encryption is based on how good the random generator is. It should suffice for short strings.

But what I'd suggest is that you can instead use the built-in MySQL 5.0 AES functions ; AES_ENCRYPT() and AES_DECRYPT()

SELECT AES_ENCRYPT('secret squirrel', '12345678') AS encoded

=> ØA;J×ÍfOU»] É8

SELECT AES_DECRYPT('ØA;J×ÍfOU»] É8', '12345678') AS decoded

=> secret squirrel

These use 128-bit AES which should be strong enough for most purposes. As others commented, using a salt value and a key with a high entropy is a good practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM