Actually I have a database that stores customer sensitive informations.
I'm using something like that to encrypt that data:
$algo = 'AES-256-CTR';
$key ='password md5 from bdd'
$iv = substr(hash('sha256',$email),0,openssl_cipher_iv_length($algo));
$data = base64_encode($data);
$data = openssl_encrypt($data,$algo,$key,OPENSSL_RAW_DATA,$iv);
As you see I'm using the customer's email to create the iv and his password md5 for the key.
So if someone hack my bdd he can decrypt the sensitive data.
Is there a better way to do, knowing that my php script needs to be able to decrypt the data for use
My ideas:
-Use a executable on the server that create/modify the password and/or the iv and does the decryption and that the php script calls for that.
-Use a second server that stores the passwords and that the php script needs to call for decryption.
I suggest you to use Argon2 to derivate $key from user's password, then use Symmetric Encryption like AES, or XSalsa20 or XChacha20 to encrypt it, alternatively you can use other derivation functions that make brute force impractical, just take care that salt is never reused, take a look at đ Vault, and libsodium-php
As you see I'm using the customer's email to create the iv and his password md5 for the key.
A few things you may nees to be aware of
IV suppose to be unique for CTR mode, but as already pointed out, it is static (derived from email).
it is possible to find md5 collision in a minute on commodity hw (newest tunelling method)
you are missing any authentication tag.
Common practice for encryption is having random IV, encrypted source and MAC as part of the ciphertext (e. g. iv.encrypted.mac)
imho creating a key from password md5 may be feasible assuming you don't store the keys, so there is nothing to find a collision against and the passwords are having high entropy (are long and random)
So if someone hack my bdd he can decrypt the sensitive data. Is there a better way to do, knowing that my php script needs to be able to decrypt the data for use
it is generally a problem storing system credentials. you may use a credential vault, but you need to store the vault credentials somewhere. you can encrypt the system credentials, but you need to store the decryption key somewhere. Ay least hide the credentials from plain sight so it makes more difficult for automated hacking tools or not so dedicated adversaries.
Related
Given that a one time pad is unbreakable (to the best of my knowledge, please feel free to correct me), if I were to generate a pad, and use this same exact pad to encrypt passwords for a website when a user is created and store the encrypted password in my database, is this a safe method? In other words, is it ok to keep this same pad forever as long as no one ever sees what the pad is?
Should I instead use something like mcrypt?
What you would do with the one-time pad is encrypting the password. Encrypting passwords is not optimal, because however you do it, you will be able to decrypt the password. Your application itself must have access to the key (or the keys since every one-time pad can only encrypt a single password), so can do an attacker if he has enough privileges.
That's why we use hash functions to store passwords, they are one-way, you can check if an entered password results in the same hash, but you cannot get the original password back. PHP offers the function password_hash() to generate such hash-values, it handles all the pitfalls with generating random salts and uses the slow BCrypt to hash passwords.
The "one time" in one time page means that a given key is only used to encrypt a single plaintext. In other words, you have a separate pad for each item you need to encrypt. That's the thing that makes them unbreakable. Since those separate pads have to be stored somewhere, you are vulnerable. Instead, use a widely used and tested library (such as mcrypt) and encrypt your passwords using a salt
I run a DV 3.5 server on MediaTemple with Linux CentOS 5, php and mysql DB and am trying to encrypt phone records with AES.
I came across what seems to be good script as PHPAES
but I am not sure of the following:
Where do I actually store the AES
Encryption key used to encrypt and
decrypt the phone number?
How do I call on the AES encryption
key when a user submits their data
via form and stores into our MySQL
database?
When I want to descrypt that information for our internal customer service agents - how do they in turn call on the AES key?
I realize this is probably very simple but please don't insult. I am trying to learn best practice for how to move forward with any type of encryption whatsoever. Something (to this point) we have not had need for.
I have developed a process where I start with an initial encryption key that I encode into a SHA1 hash, then encrypt using a with a username/password combination and store it in a database. The password (hashed or otherwise) is never stored in the database and is only used at login to decrypt the encryption key. I then use that master username/password to create additional users with passwords in which a PHP or JavaScript encodes the decryption key with the username/password of the new user and stores that encrypted key in the database. When I attempt to decrypt the encryption key from the database using a username/password combination I should expect a SHA1 hash back. If I do not get a valid SHA1 hash back that can decrypt data, then I know the password is wrong and the data is unusable. You must have a valid username/password combination to get the decryption key and that is transmitted to the client via SSL, decrypted using a JavaScript function, then stored in a cookie for the SSL session.
To circumvent the system, decrypt the data and access the information you'd have to be infected with a key-logger or trojan that scoured you cookies during that login session, otherwise the server owner nor a client without the username/password combination can use the data in the database without brute forcing it. Using AES 256-bit and strong passwords (12+ characters, A-Z, a-z, 0-9, symbols, etc) and you've got yourself a fairly difficult to breach solution, or at least one that would be painful to attempt.
Each account has a lockout feature, so if you try to login via the web too many times and fail, the account is locked out. All PHP pages encode/decode parameters to prevent SQL injection attacks and validate a PHP session is active and matches the last session tracked during you login, and also validates your encryption key works. Each time you login or visit the login page, the previous session is invalidated or if your session times out it is also invalidated. Even with all those layers its fast and prevents people from using PHP scripts that output JSON using fabricated POSTs to scripts and SQL injection attacks. It also limits the ability for the server owner/administrator to decrypt and read your information if its stored on a shared provider, etc.
I actually ended up going this route:
I encrypt the initial data with a salted hash which is stored in the database itself (and is unique to every record stored). I then take that 256bit AES encrypted string and run it through RSA encryption with my public key which sits server side.
in order to decrypt, I have to upload a temporary file with my private key and retrieve the necessary data.
quite secure in my opinion.
I have read about using MySQL AES_ENCRYPT/AES_DECRYPT (two-way encryption) is less secure than using PHP - hash() (one-way encryption).
http://bytes.com/topic/php/answers/831748-how-use-aes_encrypt-aes_decrypt
Is it true that it is more secure that 'Rather than send the User his password, simply send him a link that he can click on to reset his password, instead.'?
And on top of that, if I am using MySQL AES_ENCRYPT/AES_DECRYPT (which I quite keen on...), how do I define the key which can be accepted by MySQL? for instance, is the length of the key important? or can I simple use '123123#123123' as my key?
thanks!
There is a fundamental difference between the two concepts, hashing and encryption:
Encryption can be reversed, hashing can't (at least that's the idea).
If a malicious user gains access to the passwords in a database and knows the key you used to encrypt them, they will be able to recover said passwords. If they are hashed, they won't be able to do that.
That's why passwords should be always be hashed (and salted), never encrypted.
for instance, is the length of the key important? or can I simple use '123123#123123' as my key?
AFAIK MySQL's AES_ENCRYPT can take keys of arbitrary length; but obviously shorter keys will make it easier for an attacker to bruteforce it (ie: try all possible combinations)
Two way encryption is inherently less secure because the real data is stored somewhere. That is, you have a password "hello." Then you hash it, you get 5d41402abc4b2a76b9719d911017c592. This is meaningless to a normal person and they will not know how to decrypt it without knowing the correct encryption algorithm. They cannot use this either because only the original password is used. You check a password by hashing it and comparing it to the hash (also stored). 5d41402abc4b2a76b9719d911017c592 hashed is 69a329523ce1ec88bf63061863d9cb14, so they don't match. Even if a user knows the hashed password, he can't get anything out of it.
So you can store the encrypted data, but if you decrypt it when you are pulling it out then anyone can use it.
The security of sending a user a link compared to giving them the password is a different issue. If you email the password, it is printed out in plain text for all to see (and use). Giving them a link to allow them to input a new password means no one will see it which is a bit more secure, but if someone committing fraud has access to that link anyway it is going to cause problems.
About AES, I can't find out too much on it at a glance, but it looks like it doesn't matter what you encrypt. So if you use AES_DECRYPT(AES_ENCRYPT('x', 'b'), 'b'); it will return 'x'. You have to keep track of the key.
If you are storing passwords on your server with symmetric encryption, you have to decode the stored password to test it against a user-submitted password. That means the key also has to be stored on the server. Which means anyone who compromises your webapp can retrieve and decrypt every user's password. (And use them to compromise other accounts where the user has used the same password.)
Hashing a password means that you can't leak the password to an attacker because you don't even know what it is yourself. You can still check whether a submitted password is the same as the original password by hashing it using the same algorithm and salt, so you can still tell whether a submitted password is right or wrong, without having to know what the password is.
Using hashed passwords does mean you can't tell the user what their password was in a ârecover passwordâ option. But you don't really want to do that anyway, especially over an insecure mechanism like e-mail. One-time, time-limited reset-password links serve the same purpose with less potential damage.
For passwords, one-way hashes are almost always the way to go. One-way hashes mean that there is far less likelihood that anyone but the user would be able to know their password.
If you choose the one-way route, then you'll need to set up a password reset method. If this is done correctly, it should be fairly secure for most purposes. To gain better security, you can add things like security questions (e.g., "What is your favorite color?") that the user would have to answer before receiving a password reset link in an email.
As for keys for AES_ENCRYPT/DECRYPT-- MySQL will accept variable lengths for the key parameter to the functions, but it will use a 128-bit key regardless, so it's to your advantage to pass at least 128 bits' worth.
One-way encryption means you can only encrypt. (For example, you encrypt a password and store the result. Whenever a user authenticates, you encrypt what the user enters and compare. There is no need for a decrypt function in such a scenario.)
Two-way encryption means, there is both an encrypt and decrypt function available. In PHP, that is accomplished through the mcrypt_encrypt() and mcrypt_decrypt() functions.
An update! mcrypt is deprecated in PHP 7.1 and removed in 7.2. See OpenSSL or Sodium instead for encrypt and decrypt functions.
I'm making an application in PHP and there is a requirement that it must be possible to decrypt the passwords in order to avoid problems in the future with switching user database to different system. Consider that it's not possible to modify this future system's password method and I need plain text passwords in order to have the passwords generated.
The plan is to encrypt the user's password with a public key that is stored on the server. Authentication is done by encrypting the input and comparing the results. There is NO decryption done. The private key capable of the decryption is stored off-site for later usage.
What encryption/decryption algorithm would you suggest? Are the encrypted passwords still as safe as hashing (MD5/SHA1) when you consider the private key is not available to the attacker?
I'll rephrase Jammer's approach -
Generate a public/private key pair. Hard-code the public key on your webserver. Store the private key in a physical bank locker, outside the reach of webserver/database/any developer.
When user registers, encrypt password + salt using public key. This step is identical to using a hash algorithm. Store the encrypted password + salt in the database.
When you want to verify the password, encrypt it again, and compare it to the value stored in the database.
If an attacker gets the database, he can't decrypt the passwords because he doesn't have the private key. He cannot get the private key because it is in a bank vault outside his reach. Two identical passwords will still be stored differently in the database because of the salt.
I don't recommend using the above approach because at any point of time in the future someone could abuse the private key and get access to all passwords.
But if you guarantee that the private key will always remain private, then I don't see a technical flaw.
I could be wrong, of course.
Don't decrypt the password. If you need to change the password system in the future, add a field called storage_type (or whatever).
Then, when you need to change the passwords, you will check if it's an old password. If it is, next time they login, you can change the password encoding. Otherwise, login with the new system.
Being able to decrypt the passwords is a bad idea (and there's probably not any way of doing it that would be much better than storing them unencrypted). It sounds like your main problem is being able to use the passwords if you change your storage method. Just do what Linux does, store how you're hashing the password with the password. So for example $1$salt$hash is MD5. That way, if you decide to change how passwords are stored, you can still check against the old passwords (and if someone logs in correctly, you can update their password with the new hash).
The only problem I see is that most public-private key encryption code out there will encrypt a symmetric key using the public key, and rely on the private key decrypting that, then use the symmetric key to encrypt the message.
You want to use the public key to directly encrypt the password+salt.
So attacks against your system boil down to:
Attacks against general public/private key encryption
Attacks against your private key store.
For most applications it is more than sufficient to store SHA-1 hashes of passwords.
Yes, there are known collisions in most hashing algorithms, but that doesn't imply an actual attack vector. Especially when you're salting the hashes.
For your salt: Store it in a configuration file that is not accessible from the outside but can be read by your PHP installation.
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:
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.
Use a secure random generator to generate your keys. Weak randomness is the number one cause of encryption related security failures, not cipher algorithms.
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 (e.g. 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 (e.g. NTFS encryption).
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.
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.
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, e.g
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;
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.
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.