Encryption and hashing in MySQL versus PHP - php

I am writing some simple login scripts. I am just wondering, is there any advantage of using the hashing and encryption functions in MySQL over PHP or the other way around?
I know using a stored procedure, I could possibly be transmitting sensitive information insecure. On the other hand, it may be simpler to maintain.
Are there any benefits of using either?

The only issue I see of using mysql for encrypting sensible data is that if your web server that is running php is in a different location of your mysql, you may send sensible unencrypted data over the network that is communicating this two parties.

I would definitely use PHP over MySQL to hash passwords. There are so many ways that a query could be stored and viewed, it could be bad if there are queries like this that end up getting stored somewhere:
SELECT id FROM users WHERE username = 'User123' AND password = MD5(CONCAT('SecretSalt','MyPassword'))

If you're storing passwords, use a hashing function designed to be difficult to crack. bcrypt is a reasonable choice and many answers here go into detail on how to implement it.
MySQL's hashing methods are not as secure as this and are intended for other purposes, such as hashing documents to check for duplication.

Related

Encryption of hashed passwords?

I came about this security discussion after reading some topics about session management in php, have a look:
https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2
Quote from Chapter: To Pepper Or Not To Pepper?
A much better solution, which is especially useful if you employ hardware separation, is to encrypt the hashes before you insert them in your database. With this safeguard in place, even if an attacker finds a way to dump all of your database tables, they first have to decrypt the hashes before they can even begin to crack them. With the PHP and the database on separate hardware, this becomes much more secure.
In this article, the link to https://github.com/defuse/php-encryption is shared...
So far, I only used password_hash() in order to store passwords in a database. Is it recommendable to encrypt the hash itself? What's your opinion?
Thanks for your ideas!
Hashing with an appropriate hash algorithm is usually enough to protect the passwords, but it is indeed more secure to encrypt (not encode) the hashes afterwards.
When you encrypt the hashes with a server-side key, an attacker must gain additional privileges on the server, to get this key (without the key, the hashes are worthless). It is much easier to get readonly access to a database, than to get privileges on a server. Examples are SQL-injection, thrown away backups, discarded servers, ... In all this cases the encryption would protect the hashes.
In this answer you can find more information, or maybe you want to have a look at the end of my tutorial about safely storing passwords.
Is it recommendable to encode the hash itself? What's your opinion?
No, password_hash() / password_verify() is sufficient. People who need spinal-tap grade security can refer to that part of the article for guidance to avoid accidentally shooting themselves in the foot trying to improve their security, but in general if you're using bcrypt in 2016 then you're fine.
Unless you have separate servers for your website and for your database, the security gain by this strategy is zero. If I can get into your database, I can almost certainly get to your file system, and recover the encryption key.
If you do have separate hardware, and you use an authenticated encryption library such as the one provided by Defuse Security, do feel free to use it. Just know that it's not necessary for most use cases, as the password hashing API provides decent security against modern password cracking.
In a later version on PHP, they'll also support Argon2. If you're going to go overboard, switch to that instead of adding complexity to your protocol.
(Also, it's encrypt, not encode.)
there's no need at all to encrypt the hashes. The attacker has to reverse the hash to find the correct plaintext (user password). This is equivalent to find out the right key for the encryption. hashing is enough. plus salt obviously otherwise your schema is susceptible to rainbow table attacks

PHP password_hash() vs. Postgres crypt()

I am using a Postgres 9.3 database as a back-end for a web application. I use PHP 5.5.7 to connect to the database and return JSON for the front-end AJAX calls.
I'm trying to decide on where to put the user authentication logic.
I am not a security expert; however, I am familiar with PHP's new password_*() functions and I have a strong grasp of what is going on under the hood. I am also familiar with the Postgres Extension pgcrypto and the associated crypt() function.
My question is, does it make sense to use PHP or Postgres to hash passwords?
I was curious as to how these functions differ, so I made a password hash in PHP and then gave it to Postgres to see if Postgres uses the same algorithm. Given the same parameters, Postgres returned a different result when compared to PHP (not unexpected, but with noting).
PHP
password_hash('password', PASSWORD_BCRYPT, ["cost" => 15]);
output: $2y$15$o8JufrnVXoob2NKiEGx6.uI4O2D4VcaAmY7WtNq5zPFiJow4KohGu
Postgres
SELECT '$2y$15$o8JufrnVXoob2NKiEGx6.uI4O2D4VcaAmY7WtNq5zPFiJow4KohGu' = crypt('password', '$2y$15$o8JufrnVXoob2NKiEGx6.uI4O2D4VcaAmY7WtNq5zPFiJow4KohGu')
output: false
PHP vs. Postgres
Given that these processes are different, I wonder if one is better then the other? Is one more, or less, secure?
Some other thoughts:
I currently have all logic stored in the database (in views, functions, constraints, etc.) so if I ever need to use a different front-end I don't have to worry about missing logic. Calculating password hashes in PHP would effectively require all requests to pass through PHP to access the database.
On the other hand, putting the logic in the database would allow me the flexibility to use other connection options; however, all of the Postgres queries are logged. I can't disable the logs because of the WAL used in replication. This seems like a big security hole.
Am I on the right track here? What am I missing?
EDIT
I just looked at another message thread and found some more information.
Putting the logic in Postgres would require the database to processes and perform the hash operation. This would be a bad thing for other users and batch jobs that need those resources.
Not only would the hash slow down normal operations, it would make the whole system more vulnerable to DOS attacks.
Our simple web servers with load balancing would address both issues...
Again, am I on the right track here? What else am I missing?
For the difference between versions 2y and 2a, see this thread and the various links within it:
https://security.stackexchange.com/questions/20541/insecure-versions-of-crypt-hashes
My understanding is there was a problem with the 2a implementation in PHP until v.5.3.8, though only for strings that contained non-ascii chars. PgCrypto, as you noted, doesn't "speak" 2y for some reason, and I'd assume it suffers so such problem. (Perhaps report this as a bug?)
Apart from the points raised in the latter, you nailed the main security difference between the two in your question: systematically hashing the password within the database is convenient but implies that you send it to your database in clear text, where it can (and will) be logged — or snooped at outright, if your DB connection is not encrypted.
In an ideal world, you'd hash the password in the client app using javascript before it's even sent to PHP. The next best thing is to send it using SSL to PHP, then hash it using PHP before sending it to the DB.
Aside: I'm pretty certain that PHP's crypt can generate a (secure) 2a version hash if you need interoperability for some reason.

Hash passwords with bcrypt in the database or in php code?

I use bcrypt for password hashing everywhere in my php apps. However, there is still a choice between using bcrypt in the database or using bcrypt in php code. While I believe that using bcrypt is better than most other hashing options, is it more secure to use bcrypt via a function in the database, or via a function in php?
I would go for the second option and calculate the BCrypt hash in the PHP code.
If you place the password inside the SQL statement, there are additional possibilities it can leak. First the connection to the database must be made secure and then it could end up in log files.
If you place the hash in the SQL statement, you only have to care about a secure transfer to your application, the rest will be safe because only the hash can leak. As a bonus you do not have to care about SQL-injection and encoding/escaping issues. Another advantage is, that you are independend of the database system, you can also support databases without a BCrypt implementation (most databases do not offer a BCrypt function, or only by installing an extension).
Personally I think this could go either way:
If you say that the raw password can be sniffed from on its way to the database, the same also goes for hashes. The only security added is Security through obscurity. They don't know what hashing algorithm you are using, and when they find out, hashes can be cracked with time.
The issue is that people can sniff data from PHP to the database, not that the raw password is being sent. If you use SSL with your database, you should have no issues. (Not unless your database logs what queries has been sent, if your database does log queries, then you should hash with PHP)
An upside with database hashing would be that it's faster.

Should sensitive data be encrypted through both script and database means?

I don't know too much about encryption, but is there any benefit to encrypting the already encrypted, lets say passwords? I am aware of salts, but before this is done, does it matter or not?
Encryption is 2-way thing, when hashing is 1-way. You can decrypt encrypted sting, while you can't revert hash.
One simple, but good example is using md5 hash + salt: MD5('password' + 'random_string') - no matter PHP or MySQL you use - result is the same. So what you have here - is hash of 'passwordrandom_string', which can be unlikely matched using a dictionaries.
So every time you check the password you do:
if (md5($password . 'random_string') == $hash_from_db)
Updated: but if you really concerned about security (this usually needs to be done only if your application works with very sensitive data), and say more - you have crazy paranoia and insanity about it: there are a lot of hashing methods over the Internet. Find something with random salt (so every password can have almost unlimited amount of hashes), make few changes, combine it with other hashing algorithm - problem solved.
One thing you should know - sometimes the slower hashing works - the better. That means if you somehow have a rat-hole in login-attempts counter this will really slow down bruteforce process.
One example you can take a look on - bcrypt (it uses Java for hashing). Not saying you should use it, just an example of what you should look for.
This question has some relevant discussion on the topic. There are cases in which it would be a bad idea and could potentially weaken the encryption as pointed out in the linked thread, so you wouldn't want to do this unless you're really sure of what you're getting into.
The fundamental basis for encryption is that it's easier to encrypt (polynomial time) than it is to decrypt (non-polynomial time). The only way encryption breaks is if either/both of the following is true:
There's a vulnerability in your encryption scheme which decreases the gap between the polynomial time it take you to encrypt and the non-polynomial time you expect it to take an attacker to decrypt.
Someone has sufficient computational resources to decrypt your data (in non-polynomial time).
It sounds like there are cases where double encryption could actually make issue #1 more probable, so that's dangerous. But issue #2 seems like the bigger one to me. The idea is that an attacker with sufficient computational resources will be able to decrypt my data -- an act which implies that they're willing/able to invest orders of magnitude more computational resources to decrypt my data than I was in encrypting it.
If we accept on fiat that an attacker has the vast computational resources required to decrypt my data, the thought that they could have 2x that many resources doesn't seem unreasonable at all, to me.
And realize also that if you're using the same key, then there's really no added security whatsoever -- once they crack one, they've cracked them both. Potentially there could be value in using two different encryption techniques with two different keys to encrypt something in order to protect against issue #1 popping up for either encryption scheme, but that's surely debatable.
It depends on what you mean by encryption. If you're actually encrypting information on the database using for example Microsoft's SQL Server encryption engine, then yes it does matter. You should not rely on database level encryption as it's not really secure. The key is still stored on the machine and only prevents naive attackers who do not seek out that key along with the database.
Generally, databases also support exporting data in plaintext when the database is encrypted. This means that if an attacker gets into the system, they can just do that. If they only have the hard drives (external drive is stolen), then it saves you.
Passwords should be hashed in your application and then sent to a database, generally. It's considered secure to generate a 64 byte salt and then use SHA-512(salt || password) where || denotes binary concatenation. Don't use randomized ASCII text for salts and stick with secure random number generators such as /dev/urandom or Microsoft's CryptGenRandom. This makes it so attackers cannot store pre-computed lists of hashes for reverse lookups of common passwords.
If you want to prevent the stolen backup drive scenario you need to also make sure you are backing up your database, keeping encryption on, and storing the keys in a secure environment away from the encrypted database. We call this "separating the lock from the key". Since this doesn't help you in the situation where your database is exported, you also want to do hashing as I mentioned earlier. Hashing in addition to encryption will make it so that 1.) Attackers can't get at other less sensitive information such as names and addresses and 2.) Attackers can't even begin to attempt recovery of passwords or other credentials.
The bottom line is that it depends on your threat model.
You do get some benefits from encrypting twice using different keys. For instance, a file encrypted with weaker ciper and subsequently encrypted again with a stronger cipher and key strength will be harder to break than just having the weak cipher alone. The analogy is putting a flimsy lock box inside of a bank's vault. But, in general, you're better of encrypting with a strong cipher than encrypting twice with a weak cipher.
There are also some instances where it is appropriate for something to be encrypted twice, such as when you cross multiple trust barriers. For instance, you might encrypt your files before sending them to a cloud provider (who you may not trust). If the cloud provider needs to send files to another offsite backup company (who the cloud provide may not trust), they might encrypt it again.
That said, for passwords, you are probably better off with using a strong hash (e.g. sha1) alongside a salt for storing passwords.
Yes. It does matter. Storing sensitive data anywhere in plaintext goes beyond bad practice. It's dangerous. Even the standard md5 hash is considered "broken" now, and shouldn't be used on its own without salting it, and perhaps using other hashing combinations in tandem with it. Just to shake things up.
$salt = 'Yh%Gg^!&ud$*';
$encryption = md5(sha1($salt.md5(md5($salt.$_POST['pwd']))));
$query = mysql_query("SELECT * FROM users WHERE name=$uname AND pass=$encryption");
Not exactly the most secure, but if anybody gets their hands on the table information, they won't be able to crack it without knowing the salt and hashing combination.
Ultimately, you need to make an educated decision based on the sensitivity of the data. If you're storing user passwords of any kind, even YOU shouldn't know what they are.

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:
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.

Categories