I am trying to encrypt a plaintext string in RSA using phpseclib0.3.5.
My problem is I am trying to get rid of random padding and make it so I can manually set it. I know this is bad for security reasons, but it must be done. We are conducting a password study, and do not want to store the private key on the server. We will just be encrypting with the public key and comparing ciphertexts to test a login. Therefore, for the same plaintext, the ciphertexts must be the same everytime they are generated.
Hopefully somebody knows how to do this. I have been trying to sift through the phpseclib code but it is not friendly.
Thanks
I think you are using the wrong tools. If you want to use comparison to check if the right password is given, you should use hash functions like SHA-2 instead of public key encryption. You can do the latter in addition if for some reason you need to recover the passwords (usually a bad idea in production, but maybe useful for your setting)
Related
I currently have a private key meant to be used for signing documents which is currently stored in a keystore. Despite the server being dedicated for this task (ie: no one has access to it other than the signing service running over https) I am unwilling to keep the password for the keystore in plain text. I recognize that this could be a potential risk if anyone ever had access to the server.
What I am thinking could be appropriate would be keeping the password for the Keystore encrypted in the server with the user's password. So, whenever the user wanted to make use of the private key it would be necessary to provide his password. Password changes would not be a problem since the encrypted password could be decrypted with the old one and encrypted with the new one.
For an attacker to gain access to the PK it would be necessary to guess someone's password and have access to the server to decrypt the password to access the KeyStore. Strong password policies could be enforced to avoid weak passwords and not all users would be given the possibility to sign documents.
I was reading the OpenSSL PHP page when I found this comment (with a score of -8):
After generating a key pair with OpenSSL, the public key can be stored
in plain text format. I then encrypted the private key itself using
regular mcrypt with the human-memorizable key of my choice and
converted it to ACSII using base64_encode. Then to get the private key
back, I just decrypted it with mcrypt. This way I could store the
encrypted private key on the server without worrying about having
things stored unencrypted.
Of course, this will only be as good as your human-memorizable key is
and can potentially reduce the security of your script if you choose
something simple or don't use salts.
So, is this such a bad idea or could this be used in production?
Depends on what we call bad idea. The whole idea of security is to make violations more expensive and difficult to occur. But there sometimes exists a tradeoff where you lose usability for an added security, or in this case, there is a higher chance that more of your users might begin forgetting passwords if they should use a different password for this case rather than the same user-account passwords.
You must think if every now and then user passwords are required to be changed, users might get confused and forgetful, and then give it up and start writing their passwords in a file on their desktop! Would that really help? I know small and large organizations that give users PGP certificates where its passphrase is the same as the user's account password. So in the end it is really your choice! If it is me, if it is not the top secret documents that I am dealing with, I would use the same password as the user account.
So,
I have a mysql table which stores passwords, and these passwords can not be hashed, cause I need to recover it to plain text later.
I have a javascript, which via ajax/php takes these passwords from a mysql database and sends it to another server that will use it to authenticate, that's why i need to have them in plain text when I send.
I know there's base64 and other encryptation alghorythms, but that's unsafe.
The best solution I found is OpenSSL, but I'm not sure if I'm on the right path. Am I?
OpenSSL is a good place to start looking. It supports a very large number of secure encryption algorithms that you can use to encrypt the plain text passwords. AES-256 or Twofish are good algorithms to start looking at. 3DES is also considered sufficient to today's standards.
For good security, you will need to encrypt each user's password with a different key; that is each user has a unique encryption key to them and you do not use 1 key for all passwords. This could be a hash of the user's password that they use on your site, but often user passwords aren't strong, and if they forget the password to your site/service, then they also lose their encryption key.
For the greatest security, you shouldn't store the encryption keys anywhere. When the user logs in with their password, you can generate the encryption key in memory based on their password. Ideally it would not just be a hash of their password, but their password applied through some sort of transformation algorithm.
If that isn't an option, then you should store the encryption keys on a different physical server than the one that stores the encrypted user passwords. The server that stores the encryption keys should have a number of security and access control features in place with very controlled database access so pretty much only your application can access the keys.
And on top of that, you must disclose in your privacy policy that you may store encrypted forms of their passwords for use with the 3rd party service.
Hope that helps. OWASP may have some other helpful information related to what you are going to do.
thanks for all the answers, I'm going to use an php encryptation method described here: https://stackoverflow.com/a/6639179/1415262
and try some openssl.
For all of the other answers, I have a few problems with them and short time to explain why.
PS.: I can't up vote yet, but special thanks to #drew010 and #fabio :)
I would HIGHLY recommend that you don't store passwords in plaintext and maybe generate some kind of one-time usage key that is passed from one server to another.
So server one has a key linked to a specific user that is unique, this key is also on server two and that is the key that's passed.
I'm developing an add on to a system I have that will allow my clients websites to pull data from it to display on their sites.
Now, most of the time the information being stored in the system is not in any way sensitive (because its just published to their website) but some users have set up tables that they want to manage in the system out of convenience, but not publish on their website - though they might want to make a request on it (ie, to check a customer has a login to their website perhaps, or to grab an email address). So I need to encrypt the responses to reduce the chances of someone getting the data that shouldn't have it.
My plan is that the user will identify themselves with a user ID, a public key, and the name of a query they want to run (which they will have defined in the system itself beforehand) - and the request will take a form something like this:
require("backend-api.php");
$myUserID = "bobs-restaurant.com";
$myPublicKey = "sdg136MAGHYasfadgHGQ"; //send this with the request
$myPrivateKey = "adgljavd8i1356avdilj"; //never send this anywhere
$queryName = "LIST_OF_DISHES";
$backend = new backend-api();
$response = $backend->getData($myUserID,$myPublicKey,$queryName);
$list_of_dishes = $backend->decrypt($response,$myPrivateKey);
//user then goes on to use the data in their code or maybe just display it as-is.
It has to be simple, because the users are either not going to be seasoned PHP'ers or they're going to be time poor, and using the system to instead of having to write their own content management solution.
Assuming the above user ID and public key were a match, and the query existed and returned data - I was going to have my system encrypt the response to a private key which is known to the user ($myPrivateKey) and which is known to my system, but never exchanged between the two in a request so that it couldn't be intercepted, and I was going to use something like this reversible encryption class to do the encryption.
The problem is, I'm going to have to provide users with the decryption class so that they can get the data out of the response.
So if Mr Malicious somehow obtains a user ID and public key belonging to someone else, and he has downloaded a copy of the decryption class from the tutorial/user manual on my systems website, am I right in saying that he wouldn't need to know the private key, because he could just work out how to decrypt it from studying the code?
If the answer is yes, what is it that I haven't thought of that will prevent that from happening?
Mr Malicious wouldn't know how to decrypt anything from simply studying the code. The best he could do would be to brute force the "private key".
I put that in quotes because this actually isn't public key encryption or cryptography. This would only be public key cryptography if your server encrypted the data using the client's public key, then the client decrypted that using their private key. If that were the case, the server would have no need to know the private key at all. But it sounds like you're using the public key for something entirely different.
What you seem to be talking about here is symmetric key cryptography (using the same key to encrypt and decrypt the data).
If you use a strong encryption / decryption method, your method seems fine, but I would go with an existing algorithm such as Blowfish or AES.
A good encryption method never relies on people not knowing how it works for its security. In fact, if you're going to use encryption, you never want to try and make your own. If the method used to encrypt something is simple, like ROT13, then yes, knowing the method will allow an attacker to decrypt it pretty easily.
However, an encryption method like AES is widely known, published, and used all over the world. Everyone knows how it works, but without knowing the keys used to encrypt and decrypt, it's hard to break.
The biggest issue that you have is that both the encryption class that you linked, and AES are symmetric or pre-shared key encryption methods. This means that you must use the same key to encrypt and decrypt the data. This doesn't work for your purposes, because you and your users don't know the same key. If you have a way to know the same key, check out mcrypt for PHP
Otherwise, use asymmetric key cryptography. This works using the method you described, where something can be encrypted using a public key that anyone can know, but only decrypted by the person with the private key. The easiest version of this to use is GPG, and it's possible to use it in PHP, although probably more work to setup. See this article
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 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.