Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Hello Stackoverflow community,
I have a difficulty regarding electronic signature and especially how to apply the law of Singapore in terms of technical requirements.
By law, Singapore accepts electronic signatures if the signature is:
unique to the person using it;
capable of identifying such person;
created in a manner or using a means under the sole control of the
person using it; and
linked to the electronic record to which it relates in a manner such that if the record is changed, the electronic signature would be invalidated.
We are able to make the electronic signature via HTML5 canvas then save it as a data base64 string. That means point 1., 2. and 3. are ok.
Problem comes with point 4., we can indeed combine the signature and data in an encrypted string however, as we have both the signature and data we would be able to edit the data and create a new string:
|id|name|price|signature|final_hash|
------------------------------------
|12|test|40000|data:base|3edcde4642|
So we thought of another way, which is to mix some parameter we do not know into the final_hash such as the IP address of the signatory, his/her user agent... however, as we do not save these values, this means that we will not be able to check later on if some values were modified.
That's where we are stuck, do you have any idea on how to do this? To me, it seems impossible to satisfy both being able to check if no data was modified and being unable to modify the data later, but I may be wrong of course.
Thanks a lot for your help!
Note: I use the term "document" to refer to whatever it is you want to sign.
Some Misconceptions
First off, what you get from using HTML5 canvas or similar approaches is not a signature but a fingerprint. What I mean by that is that it identifies the client, but it cannot sign a document (at least not alone). Two caveats:
The fingerprint is of the browser, not of the user.
There will be collisions. In fact, for the sake of privacy, there is a push to make fingerprinting browsers harder, mainly because companies could use fingerprinting to track you without cookies.
You should not sign by concatenating the fingerprint to the data. That is not a signature, that is a salt, and not the best approach to salt either.
By the way, ClientJS is a good tool to create browser fingerprints
Your requirements
unique to the person using it;
capable of identifying such person;
Those requirements spell "Identification". It means you need an ID of the person.
created in a manner or using a means under the sole control of the
person using it; and
This requirement spell "Authentication". It means you need to verify that people are who they claim to be. You probably have seen the theory, you do this by: a) something the person is (i.e. biometric) b) something the person knows (i.e. a password) or c) something the person has (i.e. access to an email account, a cellphone, etc...).
It bothers me that you seem to be using browser fingerprint to do both identification and authentication. This could make it too easy to fake requests to appear to be another user.
Usually what you would do in this situation is to have a traditional authentication process with username and password, then issue a cookie. Why do you avoid that?
linked to the electronic record to which it relates in a manner such that if the record is changed, the electronic signature would be invalidated.
This requirement spell "Signature". This probably means that the electronic record will be public and we want to have protection against counterfeits.
it seems impossible to satisfy both being able to check if no data was modified and being unable to modify the data later
The general idea is to have the signature become invalid when the document is modified. If you want to make the modification official, you need to create a new signature.
In order to prevent third parties to create a signature from the modified message you need to add some secret key. Now, that raises two questions:
Should third parties be able to verify the signature? If they should, you need an asymettric algorithm.
Should the documents be protected against you (or people with rigthful access to the server)? If they should be protected, it suggests you need a secret for each user. The simplest solution is to have a password.
Asymmetric solution
To implement an asymmetric solution in PHP I suggest to use phpseclib.
Follow their example to create a key pair:
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$pair = $rsa->createKey();
$privatekey = $pair['privatekey'];
$publickey = $pair['publickey'];
The code will use entropy collected from the server to generate the key pair.
Then you can use it to do simple RSA signature and verification.
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('...'); // private key
$plaintext = '...';
$signature = $rsa->sign($plaintext);
$rsa->loadKey('...'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
If you need to verify signatures on the client, you can use jsrsasign.
For your use case, you only need one key pair for the server. The concept here is that the server is signing the document. If the signature matches, it means that the fingerprint is correct.
Either way, you should keep your private keys secret (do not ever send it to the client), and you can share the public key with whoever you need to be able to verify the signatures.
Using this, the server can sign the document that identifies the user / client (including the fingerprint, for example) and publish the document plus the signature. The document would be perfectly readable (it is not encrypted), but if somebody modifies it, the signature would no longer be valid.
To verify the signature they need the public key. However, the public key is not useful to create a fake signature, to do that they would need to get the private key.
Symmetric solution
The main advantage of a symmetric algorithm is in performance. The disadvantage of a symmetric algorithm is that you need the key to verify it.
Therefore, if you need third parties to be able to verify the signature (or even if you need the verification to happen on the client side) you would be exposing the key used to create the signatures.
In this case, the signature is easier to implement. To sign use hash_hmac with the key. To verify the signature, repeat the process and compare it with the signature you got.
A key for each user
I am not sure if you are trying to protect the data from yourself. That is, if your objective is to prevent you (or your team / or whoever manages the servers) from tampering the data.
If that is what you want, you can password protect your keys. For the asymmetric solution, phpseclib allows you to set a password used to cipher the private key (and therefore needed to be able to sign). For the symmetric solution, you can use the password directly as key.
If what you want is to generate that password from your fingerprint, you can do that with a key derivation function... yet, remember that there will be fingerprint collisions, and that having a unique browser fingerprint can be considered a privacy concern.
Key derivation functions
You can get a cryptographic key from external input (browser fingerprint, user password, etc...) using a key derivation function.
From worst to best option:
Truncate to size the fingerprint. DON'T DO THIS EVER. This wastes entropy from the fingerprint. It makes collisions very likely.
Hash the fingerprint. DON'T DO EITHER. Anybody who can take the fingerprint and knows what hash you use can get the key.
Hash the fingerprint concatenated with pepper (the same salt for everybody). Still not good
Hash the fingerprint concatenated with salt (unique for each individual). Many say this is the first sane option. Yet, you should keep in mind to use a good hashing algorithm.
Use a hash based message authentication code. hash_hmac. For me, this is the first sane option. It costs you virtually nothing to change a hash of a concatenation to a call to hash_hmac. In addition, it will protect you from partial preimage vulnerabilities of the hash algorithm (known or to be discovered).
Use a dedicated key derivation algorithm. Among the standard PHP function, I suggest hash_hkdf or hash_pbkdf2.
Note: sometimes people (including me from the past) refer to these key derivation functions as hash functions. The reason is that when you use them, you do not call your regular hash function (directly) and what they give you is technically a hash. Yet, in reality, they are not hashing algorithms; they are algorithms build on top of hashing… in fact, one of the parameters to use them is what hash function to use internally.
Related
User's content is encrypted, but needs to be decrypted. There are multiple files that need decryption to be viewed, and they will definitely not be viewed at the same time.
I am currently encrypting by using the user's plaintext password to encrypt a randomly-generated key, which encrypts the user's data. The password is hashed and verified normally before doing anything. I am using PHP's aes-128-gcm openssl_encrypt() function.
My current system requires a password every time the user wants to read a file.
I have thought about decrypting all of the content at one, but this doesn't scale well. I have also thought about storing the user's key as a cookie, but I'm worried about security.
Is there a standard way to do this?
Thanks!
The first thing to do is separate the users password out of this. You'll have to decrypt and re-encrypt all their files. There may be other ways around this such as allowing only new files to use this system. But that is very use case specific, such as how long do you keep their files, what is the turn over on them etc..
In any case this is a way to do that:
Encrypt the files they submit using a password you generate.
Store this password in another file we'll call it key.txt for now. Encrypt this file using the users password.
When user logs in (if they don't have it stored) take their password, decrypt key.txt and get the generated password.
Now you can save this generated password anywhere you want, without affecting the users account.
What they see (the end user experience) will look like always they go to downlaod a file, put their password in and get the file. They wont ever know you did this, which is nice for them.
So problem one is fixed.
Now where should we store this?
You could simply store it on the server in the DB. This sort of depends on how confidential the data is, and how secure your server is. Your ultimately responsible for the security of someone else's data, at least this way you can control it.
Make a table with these fields
user_id | ip | password | last_access
When a user goes to download a file, check their last access time and IP address to invalidate the password and make them refresh it. This is very easy to setup and totally under your control. If you save the encryption key, it will always have some level of vulnerability at least this way its all under your control.
Even if you don't want to store it in your DB, the biggest disadvantage here is if someone gets a hold of that table, but if they do that and your storing important data you probably have plenty of problems already.
At least use the first part as that solves a big problem with tying this to their actual account password. Even if a hacker gets the file password from the client (stolen cookies etc.) because it's separate, having that alone wont let them login to your site like the account password would. I am assuming here, a user must login to even get to the download part. Using the same password for both gives them them access to both the means of the getting this data and the method to download it.
To be clear, their is an argument to be made about storing it on the client side. Then if your site is compromised there is less chance someone could get a hold of the password as it (depending how you do it) only exist in memory on both the client and server etc. It puts the responsibility on them.
ASYMMETRIC ENCRYPTION
You could also use asymmetric encryption. Currently it looks you are using AES, which is fine, but it's a Symmetric Key block cypher. Basically there are three common forms of "encryption" (in vernacular):
Hashing (which really isn't encryption) - md5, sha1, sha256 - these are one way, can't be decoded. They have fixed lengths, and always encrypt to the same thing. It's common to see this for file checksum (for validating the contents of the file), Block Chain, Passwords or anything else where you need to compare two "encrypted" values.
Symmetric - AES, 3DES, Blowfish, Twofish - anything you need to encrypt and decrypt. The same key can do both. Generally these will encrypt the same thing to different values each time, because of the IV.
Asymmetric - SSL, DSA, RSA, PGP, used in Crypto currency wallets, TLS etc. With these you have 2 keys, a public one and a private one. The keys cannot decrypt their own encrypted data, only the other key can. So with this if you have one key on the server and the client has the other. You can encrypt their files using your key (decryptable by only their key) and you don't have to worry so much about someone getting your key as it won't allow them to decrypt the files. You can give one key to the client, who can use that key to decrypt their data you encrypted (even without your key). These also encrypt to different "Stuff" each time you use them.
So you can see Asymmetric form has a few advantages to use in a two(or more) party system. It also has the benefit that you don't need their key to encrypt a file. All you need is your part of the pair. So for example if you generate data for them and wan't to encrypt and later have them decrypt it with the same system, you can do that with no issues. This probably eliminates a step, as you would need to ask them, or keep track of their Symmetric anytime you wanted to encrypt something. Here you just need your part of the key pair.
It really isn't much harder to implement (on the server), its just harder to understand what it does. That's why I decided to add this, without this knowledge (which you may or may not already know) it's hard to use these terms and have them make sense. The only real disadvantage for you (if you call it that) if you used Asymmetric encryption, is if a client loses their key you would have no way to decrypt the file. So I would make sure they know to back them up in a secure place. It's the same problem that you see in the news when it comes to losing a crypto currency wallet which is encrypted Asymmetrically
As I said most of my knowledge has to do with encrypting and dealing with data on a server. So I am not sure how to tie that in to the "client experience". I do know for example how to use RSA keys for password less login for SSH etc. Which is kind of the same thing but not quite.
Hope it helps!
they will definitely not be viewed at the same time
Wouldn't the most secure answer here be to simply require the password every time? I would assume (although I'm sure this isn't the answer you're looking for) that simply asking for the password each time might be the best solution.
Although it may be tedious for the user, I would also assume it imparts some sense of security - since it's not quite as simple as logging in (as the files are encrypted).
From my perspective, I would argue that encrypted files should not be mass decrypted anyways?
Sorry, I know this isn't the answer you're looking for - but if you have more information about your motivation, maybe then a more reasonable solution can be found?
Don't do decryption on the server-side - do it client side. It is safe to keep the user's password in memory on their own device.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 5 years ago.
Improve this question
I'm currently building a web application(PHP/MySQL) which saves data from persons. Most of this data is not worth protecting with encryption but some of it is financial information like income and so on. It is not a payment application and does not store information that could be turned into money directly like credit card information but still stuff you don't want to have in a possible leak. This platform has to be sold to customers that want "security", but that can mean anything as the customers themselves do not have any knowledge what they really want, since they are business people and not cryptographers(like me neither).
It is a management platform so the people that have their financial data saved there are not the users of the platform. Users of the platform are merely a login with permissions attached to them. The server itself never has to have access the data. Every operation is done by a user(could also be an admin) that is logged in. Multiple users need to have access to the same data given they have enough permissions.
My question is now how I can protect the financial data from these threats:
Somebody finds an SQL-injection and dumps all tables remotely
Somebody steals the hard drive of the server (database + code)
Where I'm certainly not going: Large scale sniffing attack or compromised servers(like sniffing all traffic on the server itself where SSL doesn't matter) or social engineering/phishing.
I would also like to have a quick summary how much more information(keys, data, etc.) I have to store in comparison to the current system, where there is one simple field for income etc. and a standard login system with username and hashed password.
EDIT: Reformulated question almost entirely following the suggestion of comments/answers
Here are two approaches to this:
1) Use symmetric encryption because you have already arranged a secret with the client, which is their password.
Whenever the user requires access to their sensitive information, they need to provide their password. If you require this, then you can use that password as a basis of generating the encryption key.
You can use the openssl functions in PHP to encrypt the sensitive data, and decrypt it when the client needs it. This will allow you to select an appropriately hard to break algorithm which OpenSSL supports. The drawback of this is that you will need explicit user permission and their password to access that data, which is good if you're only storing it on behalf of that user, but bad if you need to pass it on to someone else.
This way you will not need to store additional information in the database. In case someone steals your hard drive, all they will have is encrypted sensitive data and hashed passwords. The drawback is that it's a single point of failure, if they break the encryption they also get the password and vice-versa however the difficulty of breaking the encryption is not as high as reversing a hash. It also relies on strong passwords, which as we know users often don't tend to use, however that's not a new problem and one we're not likely to solve today.
2) Require the user to generate a private-public key pair and send you the public key. You can then store this public key and encrypt data using it. This generally would work well if you had an app/software that communicates with your server, which can do this on the user's behalf, but is harder to implement in a web application. Perhaps there's JavaScript libraries that can do this but since it's not something that is commonly done you need to be 100% sure the library you're using is secure. However this also requires of the user to store the key somewhere and be able to use it whenever they want access to that data (again JavaScript can do this for the user but saving and loading the key is something that requires user interaction due to security concerns).
In short:
Symmetric encryption would only be secure if the encryption key is not stored on the server but is something that the user can provide whenever it is needed.
Asymmetric encryption is even more secure but unrealistic in a web application targeted to an average user.
So I would suggest symmetric encryption using the user's password as a key.
From your question, the following key points stand out.
The server itself never has to have access to the data.
Multiple users need to have access to the same data given they have enough permissions.
Maintain security even if:
Somebody finds an SQL-injection and dumps all tables remotely.
Somebody steals the hard drive of the server (database + code).
This is possible to achieve, but not trivial. The thing that makes this possible is the fact that the server does not require access to the data. This allows us to use user passwords to derive keys.
Each level in your permission structure will have an associated key. This key will be used to encrypt data that can be viewed with those permissions. When the first administrative account is created, generate a key for each level in your permission structure and use the administrative password as an input for a KDF and derive a key. Use this password-derived key to encrypt each permission key and store the resulting ciphertexts alongside the administrative account.
As new users are created and assigned ranks by the administrative account, pull the highest level permission key that the new user will have access to, as well as any keys at a lower permission, decrypt them with the administrative password (which will be required for creating users) and then encrypt them again with the new users password and store alongside the new user in the database.
This system allows you to pass the required encryption keys to each user and makes accessing data above the users permission level cryptographically impossible.
At this point, it is rather straight forward for you to allow users to access data by simply taking their password, decrypting the relevant permission key and then using that key to decrypt the data. Users changing their password is also trivial as it simply means you have to decrypt the permission keys with the old password then re-encrypt with the new password.
At a more technical level, I would recommend the following:
Use AES. AES-256 tends to be the most common but AES-128 is just as secure in the grand scheme of things. Use of an authenticated block mode (GCM) isn't as important here but is still recommended. If not, use a mode like CBC or CTR with an HMAC.
Never use a password directly as a key. Use PBKDF2 to generate keys from passwords. Using AES-256 fits in nicely here because you can use SHA-256 as the primitive to PBKDF2 and get output the same length as the internal hash function.
Generate a new random IV every time you encrypt using a CSPRNG. Prefix the IV to the ciphertext. Don't derive an IV from PBKDF2 like the key.
Asymmetric encryption and hybrid encryption are pointless here unless the users generate and retain ownership of the private keys themselves. I infer from the rest of your question that this isn't the case.
Assuming you want to be able to view this encrypted information without user interaction (e.g. you aren't just storing this information for the user and the information is relevant to your business operations), you have limited storage options.
If your exact threat model is to protect this data in the event of a database leak and nothing else, symmetric encryption is perfect, if properly implemented.
The implication of this is that the symmetric key must be stored on servers that make requests to the database and serve the data to your other (likely front-end) systems. If any of those servers were to become compromised, then the encrypted data will be leaked.
In summary, use symmetric encryption, but understand that it will only protect you directly from a database leak through something like SQL injection or a similar attack. A compromised server is a compromised server and generally means full data access given enough time.
EDIT: If you intend to require user interaction to view the secured data, then apokryfos's comment above accurately details what to do to secure the information. Generate a symmetric key from the users password and use this to encrypt an additional symmetric key. Use this secondary symmetric key to actually encrypt the data. Using two keys makes a user password change easier.
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'm looking to store some fairly sensitive data using PHP and MySQL and will be using some form of reversible encryption to do so since I need to get the data back out in plain text for it to be of any use.
I'll be deriving the encryption key from the users' username/password combination but I'm stumped for what to do in the (inevitable) event of a password being forgotten. I realise that the purpose of encryption is that it can only be undone using the correct key but this must have been addressed before..
I'm trying to get my head around whether or not public key cryptography would apply to the problem but all I can think of is that the private key will still need to be correct to decrypt the data..
Any ideas?
It's not clear what you are striving for, so advice on how to implement it is hard.
Standards like PGP and S/MIME encrypt each message with a new symmetric key. Those keys are then encrypted for each recipient of the message. This way, instead of duplicating the message (which could be very large) for each recipient, everyone gets the same ciphertext, and only the key (which is small) is duplicated—but encrypted differently for each recipient.
Maybe you could do something similar here, encrypting the key with the user's password, and encrypting another copy with your public key. If the user forgets their password, you can recover the message for them (after an appropriate backup identity verification) using your private key.
The conventional solution is to have a "recovery agent": one user that holds a second password that can be used to decrypt all data. Strict usage policies would apply to using the recovery password, such as putting it physically into a safe.
Then, either encrypt all data twice: once with the user key and once with the recovery key; alternatively, generate a session key for every set of data, and encrypt the data only once, but the session key twice.
For that to work, at least the key of the recovery agent must be asymmetric, since the private part will live in the safe, and the public key in the software.
As yet another alternative using the same scheme: encrypt the user's passwords with the recovery key on password change. This is simpler to implement, but will allow to recover the passwords and not just the data, which may be undesirable.
I'm looking to store some fairly
sensitive data using PHP and MySQL and
will be using some form of reversible
encryption to do so since I need to
get the data back out in plain text
for it to be of any use.
Protecting sensitive data is good. Now:
Whose data is it? (yours, your user's, or a third party?)
What does it need to be protected from? (disclosure, corruption (accidental or intentional...)
Who does it need to be protected from
Uninvolved parties goes without saying.
Do you need / want to avoid accessing the plaintext data yourself (useful for deniability),
Do you need to protect either your user's data from being visible to a third party,
Or a third party's data from the user,
Or your data from the user or a third party?
What are likely attacks?
Do you need to protect in the case where the server is completely compromised?
Do you need to protect against an application level attack where the user simply gains access to some but not all available data (e.g. access to the SQL database, but not the filesystem)?
Will the amount of data be small enough that the attacker can guess and simply check whether he/she got it right? (short passwords, numbers, simple words, fixed form text are likely candidates)
Will the attacker have known plaintext with which to attack?
Is it better for the data to go away (or to re-retrieve the data) if the user forgets their password, or is it worth an increased risk of exposing the data to avoid that cost?
There are probably other questions, but this is the type of thing you want to think about when using encryption. The answers will help you figure out what you need vs. what you want, and will probably help point in the right direction. You might not want to share all of the answers with us.
I'll be deriving the encryption key
from the users' username/password
combination but I'm stumped for what
to do in the (inevitable) event of a
password being forgotten. I realise
that the purpose of encryption is that
it can only be undone using the
correct key but this must have been
addressed before..
You might have decided on a solution without considering the impact. That doesn't mean the solution is wrong, but this question suggests you should think about what you are willing to risk for security. Sometimes data will be risked.
I'm trying to get my head around
whether or not public key cryptography
would apply to the problem but all I
can think of is that the private key
will still need to be correct to
decrypt the data..
This too sounds like a solution in search of a problem. Public key cryptography is useful when you have two (or more) separate actors with an interest in communicating data between them. Those actors can be real (people) or functional (components of a system), but without two actors, there is no reason to have a separate public and private key.
Basically, if you encrypt something, and lose the encryption key, you're screwed.
When it comes to securing data, you need to consider why you're securing it, and what you're attempting to secure it against. And what tradeoffs are worth making in order to do so - the only truly secure system is one that is completely isolated from the internet, which is a level of security that is self-defeating for most applications.
So here are some questions to ask yourself:
If someone compromises my database, is it acceptable for them to be able to access this data?
What if someone compromises my entire application stack?
If the answers to the above two questions are "no", then the key material must be held by the user. And they will lose access to their data if they lose the key.
You can provide an option for manual key recovery if you also have a "master key" that you don't store anywhere near your application, only you hold it and you use it to manually reset passwords. If that's also not an option (say, only the user should be able to access the data, not the system administrator), then you're going to have to make a compromise somewhere.
This is a question I have thought about myself and as I see it the following options are available (with option #1 being the most secure):
Provide no reset password functionality - if they have forgotten their password then they are locked out.
Generate a new secure master key and encrypt & hash the user's key with this master key and store the cipher text and hash result in the database. The secure key is then made known to the user either by adding it to a file that the user downloads, emailing to the user or displaying the secure master key on screen. To reset the password the user would have to enter this master key which is then hashed and compared and if they match, the user's key in the database is decrypted.
Ask the user to provide 2 security questions and answers when registering; hash the answers and store the questions and answer hash in the database. The second answer is used as the master key to encrypt the user's key. To receive a password reset request email the user has to answer the first question correctly. Once they click the link in the email the web page then asks the second question, if this is correct and the query string parameter values are valid then use the answer to the second question to decrypt the user's key.
Use an application global master key (maybe stored in the web/UI application and use this to encrypt and store the user's key. Once a user is verified through a password reset email process the user's key is decrypted using the application global master key and then reencrypted with their new password.
In summary, the benefits of each option is as follows:
This is the ultimate for security and would possibly be the only option if the data was critical to be kept encrypted. However, in the real world people forget their passwords as sure as the sun rises and not providing a reset password function could be a bad commercial decision.
This is secure as the master key is not stored on the front end or database so if the platform is compromised then the data would require some significant effort to decrypt. However, the downside is the user could still lose their master key anyway.
The weakness here is if the database is compromised the answer to the question could be researched and then used to decrypt the users encrypted key.
This approach leaves the application key in the stack leaving your data vulnerable if your platform is hacked. The only protection you have is that if the database server is hacked then the data would still be safe.
As with most things in the world of software development you need to consider what is best for what you are trying to accomplish and aim for the correct balance.
Why are you using a different key for every user?
If you choose one key, it is much easier to handle.
Store your encryption key outside of the database.
Your application will still have to have access to it, but someone with a db dump will not be able to read the encrypted info.
Generate a random session key.
Use the session key to encrypt the data.
Encrypt the random key with any number of user passwords that you need.
This way you can use any user password to decrypt the data.
Since this question is rather popular, I thought it useful to give it an update.
Let me emphasise the correct answer as given by AviD to this question:
You should not store any data that needs encrypting in your cookie. Instead, store a good sized (128 bits/16 bytes) random key in the cookie and store the information you want to keep secure on the server, identified by the cookie's key.
I'm looking for information about 'the best' encryption algorithm for encrypting cookies.
I hava the following requirements:
It must be fast
encrypting and decrypting the data will be done for (nearly) every request
It will operate on small data sets, typically strings of around 100 character or less
It must be secure, but it's not like we're securing banking transactions
We need to be able to decrypt the information so SHA1 and the like are out.
Now I've read that Blowfish is fast and secure, and I've read that AES is fast and secure.
With Blowfish having a smaller block size.
I think that both algorithms provide more than adequate security? so the speed would then become the decisive factor.
But I really have no idea if those algorithm are suited for small character string and if there are maybe better suited algorithm for encrypting cookies.
So my question is:
What encryption algorithm is best for encrypting cookie data?
Update
To be more precise, we want to encrypt 2 cookie: one with session information and the other with 'remeber me' information.
The platform is PHP as apache module on Linux on a VPS.
Update 2
I agree with cletus that storing any information in a cookie is insecure.
However, we have a requirement to implement a 'remeber me' feature. The accepted way to go about this is by setting a cookie. If the client presents this cookie, he or she is allowed access the system with (almost) equal rights as if he/she presented the valid username password combination.
So we at least want to encrypt all data in the cookie so that it:
a) malicious users can't read it's contents,
b) malicious users can't fabricate their own cookie or tamper with it.
(All data from cookies is sanitized and checked for validity before we do anything with it, but that's another story)
The session cookie contains a sessionId/timestamp nothing more. It could probably be used without encryption, but I see no harm in encrypting it? (other than computation time).
So given that we have to store some data on in a cookie, what is the best way to encrypt it?
Update 3
The responses to this question made me reconsider the chosen approach. I can indeed do the same without the need for encryption. Instead of encrypting the data, I should only send out data that is meaningless without it's context and cannot be guessed.
However, I'm also at a loss:
I thought that encryption enabled us send data out in to the BigBadWorld™, and still be (fairly) sure that nobody could read or tamper with the it...
Wasn't that the whole point of encryption?
But the reactions below push toward: Do not trust encryption to accomplish security.
What am I missing??
No real reason not to go with AES with 256 bits. Make sure to use this in CBC mode, and PKCS#7 padding.
As you said, fast and secure.
I have read (not tested) that Blowfish may be marginally faster... However Blowfish has a major drawback of long setup time, which would make it bad for your situation. Also, AES is more "proven".
This assumes that it really is necessary to symmetrically encrypt your cookie data. As others have noted, it really shouldnt be necessary, and there are only a few edge cases where there's no other choice but to do so. Commonly, it would better suit you to change the design, and go back to either random session identifiers, or if necessary one-way hashes (using SHA-256).
In your case, besides the "regular" random session identifier, your issue is the "remember me" feature - this should also be implemented as either:
a long random number, stored in the database and mapped to a user account;
or a keyed hash (e.g. HMAC) containing e.g. the username, timestamp, mebbe a salt, AND a secret server key. This can of course all be verified server-side...
Seems like we've gotten a little off topic of your original, specific question - and changed the basis of your question by changing the design....
So as long as we're doing that, I would also STRONGLY recommend AGAINST this feature of persistent "remember me", for several reasons, the biggest among them:
Makes it much more likely that someone may steal that user's remember key, allowing them to spoof the user's identity (and then probably change his password);
CSRF - Cross Site Request Forgery. Your feature will effectively allow an anonymous attacker to cause unknowing users to submit "authenticated" requests to your application, even without being actually logged in.
This is touching on two separate issues.
Firstly, session hijacking. This is where a third party discovers, say, an authenticated cookie and gains access to someone else's details.
Secondly, there is session data security. By this I mean that you store data in the cookie (such as the username). This is not a good idea. Any such data is fundamentally untrustworthy just like HTML form data is untrustworthy (irrespective of what Javascript validation and/or HTML length restrictions you use, if any) because a client is free to submit what they want.
You'll often find people (rightly) advocating sanitizing HTML form data but cookie data will be blindly accepted on face value. Big mistake. In fact, I never store any information in the cookie. I view it as a session key and that's all.
If you intend to store data in a cookie I strongly advise you to reconsider.
Encryption of this data does not make the information any more trustworth because symmetric encryption is susceptible to brute-force attack. Obviously AES-256 is better than, say, DES (heh) but 256-bits of security doesn't necessarily mean as much as you think it does.
For one thing, SALTs are typically generated according to an algorithm or are otherwise susceptible to attack.
For another, cookie data is a prime candidate for crib attacks. If it is known or suspected that a username is in the encrypted data will hey, there's your crib.
This brings us back to the first point: hijacking.
It should be pointed out that on shared-hosting environments in PHP (as one example) your session data is simply stored on the filesystem and is readable by anyone else on that same host although they don't necessarily know which site it is for. So never store plaintext passwords, credit card numbers, extensive personal details or anything that might otherwise be deemed as sensitive in session data in such environments without some form of encryption or, better yet, just storing a key in the session and storing the actual sensitive data in a database.
Note: the above is not unique to PHP.
But that's server side encryption.
Now you could argue that encrypting a session with some extra data will make it more secure from hijacking. A common example is the user's IP address. Problem is many people use the same PC/laptop at many different locations (eg Wifi hotspots, work, home). Also many environments will use a variety of IP addresses as the source address, particularly in corporate environments.
You might also use the user agent but that's guessable.
So really, as far as I can tell, there's no real reason to use cookie encryption at all. I never did think there was but in light of this question I went looking to be proven either right or wrong. I found a few threads about people suggesting ways to encrypt cookie data, transparently do it with Apache modules, and so on but these all seemed motivated by protecting data stored in a cookie (which imho you shouldn't do).
I've yet to see a security argument for encrypting a cookie that represents nothing more than a session key.
I will happily be proven wrong if someone can point out something to the contrary.
Security Warning: These two functions are not secure. They're using ECB mode and fail to authenticate the ciphertext. See this answer for a better way forward.
For those reading through wanting to use this method in PHP scripts. Here is a working example using 256bit Rijndael (not AES).
function encrypt($text, $salt)
{
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
function decrypt($text, $salt)
{
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
Then to save the cookie
setcookie("PHPSESSION", encrypt('thecookiedata', 'longsecretsalt'));
and to read on the next page:
$data = decrypt($_COOKIE['PHPSESSION'], 'longsecretsalt');
Fast, Encrypted Cookies with Libsodium
If you need fast, secure encrypted cookies in PHP, check out how Halite implements them. Halite relies on the libsodium PECL extension to provide secure cryptography.
<?php
use \ParagonIE\Halite\Cookie;
use \ParagonIE\Halite\Symmetric\Key;
use \ParagonIE\Halite\Symmetric\SecretKey;
// You can also use Key::deriveFromPassword($password, $salt, Key::CRYPTO_SECRETBOX);
$encryption_key = new SecretKey($some_constant_32byte_string_here);
$cookie = new Cookie($encryption_key);
$cookie->store('index', $any_value);
$some_value = $cookie->fetch('other_index');
If you cannot install PECL extensions, ask your sysadmin or hosting provider to do it for you. If they refuse, you still have options.
Secure Encrypted Cookies in PHP, Hold the Salt Please
The other answers instruct you to encrypt your data with openssl or mcrypt, but they're missing a crucial step. If you want to safely encrypt data in PHP, you must authenticate your messages.
Using the OpenSSL extension, the process you would need to follow looks like this:
Preamble
(Before you even think about encryption) Generate a 128-bit, 192-bit, or 256-bit random string. This will be your master key.
Do not use a human-readable password. If you, for some reason, must use a human-readable password, ask Cryptography SE for guidance.
If you need special attention, my employer offers technology consulting services, including development of cryptography features.
Encryption
Generate a random Initialization Vector (IV) or nonce. e.g. random_bytes(openssl_cipher_iv_length('aes-256-cbc'))
Use HKDF or a similar algorithm for splitting your master key into two keys:
An encryption key ($eKey)
An authentication key ($aKey)
Encrypt your string with openssl_encrypt() with your IV and an appropriate modate (e.g. aes-256-ctr) using your encryption key ($eKey) from step 2.
Compute an authentication tag of your ciphertext from step 3, using a keyed hash function such as HMAC-SHA256. e.g. hash_hmac('sha256', $iv.$ciphertext, $aKey). It's very important to authenticate after encryption, and to encapsulate the IV/nonce as well.
Package the authentication tag, IV or nonce, and ciphertext together and optionally encode it with bin2hex() or base64_encode(). (Warning: This approach might leak cache-timing information.)
Decryption
Split your key, as per step 2 in encryption. We need the same two keys during decryption!
(Optionally, decode and) unpack the MAC, IV, and ciphertext from the packed message.
Verify the authentication tag by recalculating the HMAC of the IV/nonce and ciphertext with the user-provided HMAC by using hash_equals().
If and only if step 3 passes, decrypt the ciphertext using $eKey.
If you want to see how this all looks together, see this answer which has sample code.
If this sounds like too much work, use defuse/php-encryption or zend-crypt and call it a day.
Remember Me Cookies
However, we have a requirement to implement a 'remeber me' feature. The accepted way to go about this is by setting a cookie. If the client presents this cookie, he or she is allowed access the system with (almost) equal rights as if he/she presented the valid username password combination.
Encryption is actually not the correct tool for this job. You want to follow this process for secure remember me cookies in PHP:
Generating a Remember Me Token
Generate two random strings:
A selector which will be used for database lookups. (The purpose of a random selector instead of just a sequential ID is to not leak how many active users are on your website. If you're comfortable leaking this information, feel free to just use a sequential ID.)
A validator which will be used to authenticate the user automatically.
Calculate a hash of validator (a simple SHA-256 hash will suffice).
Store the selector and the hash of the validator in a database table reserved for automatic logins.
Store the selector and validator in a cookie on the client.
Redeeming a Remember Me Token
Split the incoming cookie into the selector and validator.
Perform a database lookup (use prepared statements!) based on selector.
If a row is found, calculate a hash of the validator.
Compare the hash calculated in step 3 with the hash stored in the database, once again using hash_equals().
If step 4 returns true, log the user in to the appropriate account.
This is the strategy that Gatekeeper adopted for long-term user authentication and it is the most secure strategy proposed to date for satisfying this requirement.
You can achieve what you want securely by using AES in EAX mode. The ciphertext will be larger than the plaintext; that's normal for secure encryption.
The attacker will of course know the length of your plaintext from the ciphertext, but they shouldn't be able to determine anything else.
Generate AES keys randomly.
Be sure and use a fresh nonce for each encryption, and use the "associated data" field to ensure that a thing you encrypted for one purpose isn't presented as being for another (so things like the user name and cookie name could go in there)
the reactions below push toward: Do
not trust encryption to accomplish
security.
More "if you're not an encryption expert you'll underestimate how easy it is to get wrong". For example, AFAICT no-one else in this thread has discussed chaining modes or message integrity, which covers two common beginner's mistakes.
While both a very strong ones, AES is a standard.
As for security of small chunks of data: the smaller - the better. The less encrypted data is exposed, the longer you can use the key. There is always a theoretical limit of how much data can be encrypted within one key of given algorithm without exposing system to risks.
As pointed out a few times in previous comments, you must apply integrity protection to any ciphertext that you send out to the user and accept back. Otherwise the protected data can be modified, or the encryption key recovered.
Especially the PHP world is full of bad examples that ignore this (see PHP cryptography - proceed with care) but this does apply to any language.
One of few good examples I've seen is PHP-CryptLib which uses combined encryption-authentication mode to do the job. For Python pyOCB offers similar functionality.
Why do you want to encrypt the cookie?
As I see it, there are two cases: either you give the client the key, or you don't.
If you don't give the key to the client, then why are you giving them the data? Unless you're playing some weird game with breaking weak encryption (which you're explicitly not), you might as well store the data on the server.
If you do hand the client the key, then why do you encrypt it in the first place? If you don't encrypt the communication of the key, then encrypting the cookie is moot: a MITM can look at the cookie and send you any cookie he wants. If you use an encrypted channel to the client, why the extra overhead of encrypting the stored data?
If you're worried about other users on the client's machine reading the cookie, give up and assume the browser sets good permission bits :)
If you encrypt the cookie, the server still has to decode it to read it (to check for same key), therefore any encrypted cookie is pointless, because if stolen (and un-edited) it will still lead the hacker right to your account. Its just as unsafe as no encrypted at all.
I believe the real issue of someone stealing your cookie is the connection between the server and client. Use SSL connection provided by your host.
As for your cookie, you need to make a long random id per user in the database, (have it change every log on) and just set that as the cookie or session. The cookie that contains the key can be checked via php and if it is equal to an account or table in your database, dump the data on the web page like normal.
AES (also known as Rijndael) is the most popular. The block size is 128-bits, that's only 16-bytes, and you're talking "around 100 characters".
I think that "giving away" any data even encrypted when it is about username and password is not good ...
There are many JS that can sniff it ...
I suggest you create in users DB table a field cookie_auth or whatever ...
after first login gather : current: browser, IP,ans some own salt key, plus your hostname var ...
create a hash and store in that field ...
set a cookie ...
when cookie "responds" compare all of these with the stored hash and done ...
even if someone "steal" a cookie they won't be able to use it :-)
Hope this helps :-)
feha
vision.to
In addition, I have tried the mcrypt_encrypt and one thing please keep in mind. If you do base64_encode(mcrypt_encrypt(...)).
and then later, you do base64_decode and output the encrypted data (echo). You probably will be screwed and not seeing anything. However, if you do mcrypt_decrypt( ... base64_decode($value) ). You will see the original data.
So many terrifying things been said, which is true though, but let's see the bright side, a little common sense and continuous watch over your site might save you all the time.
Saving cookies is an important part of web development so one can't ignore it. But also we should avoid as much as possible; I see the use of Cookies only if I want to extends the login session even after user close the browser. If ones don't want to extends the user session beyond browser closing, then Session component should be used. Even with Session component usage one should be aware of Session Hijacking.
Anyways, back to Cookie thing; In my opinion if one's follow the following precautionary measurement, I am pretty sure we can be on the safer side.
I divide the precautionary measurement in to two phase
Phase1: Development
Set path attribute
Set expiration_date
set secure, httpOnly attributes
Use latest encryption Algorithms
Use two algorithms: for instance use blowfish and then use base64_encode on top of it.
Phase 2: Operation/Audit
Periodically make site audit, using tools like burp.