I am currently working on a better login-routine for one of my websites and I wanted to adress securely transmitting login-Data to the server. There are several articles and posts concerning this topic, but they frequently have quite different opinions on how it has to be done.
Starting off with SSL, pretty everyone is on the same page here: You should use SSL, and I do. Then there are those who say: "That is enough, with SSL you can send username & PW as plaintext". I disagree. Other say that it should still be hashed. And I read several posts where I had the feeling that people were concerned about different aspects of a login routine and proposed mechanisms to only handle their security aspect.
So what I want to know, if the routine I have so far elaborated would be adequate, too much or too little. I will try to explain why I chose to implement a certain feature and which security aspect I try to cover:
SSL:
The communication between server and client should always be https:// - nevertheless I read several articles warning that SSL is "no silver bullet", but it is a good start.
Hash PW clientside (SHA3, ARGON2i, BCRYPT):
Many comments did reject hasing PW. Using a hash, comparing it to a HASHed PW in the database would simply change the PW from the userinput to the HASH - an attacker would still have access by simply getting hands on the HASH. I agree. BUT (this is what I meant that people read about different aspects of security) the ones claiming that it is better than sending plaintext because in that case ONLY your system, but not other systems with the same PW would be compromised (unless, of course they use also hashed PWs). So I would implement HASHing of the password before sending it via SSL.
encrypting the HASH:
Let's assume SSL could not hide the data we send to the server and an attacker would read the HASHed PW. The only way that I could think of to adjust the security to this scenario, would be to encrypt (e.g. AES CBC) the client-side HASHed PW with a key that has been send beforehand by the server, and that has a short expiration period. The key would have to be generated randomly. Like that, the server can decrypt the data, and then compare the HASH with the one it its database.
To sum it up:
-> Clients wants to Login via SSL -> Server sends back a key -> Clientside hashing of the PW -> clientside encryption of teh HASH with the key and a random IV -> Server decrypts the data with the key (stored in $_SESSION, with an expiration timestamp) and compares the HASH with the HASH in his DB (if the expiration timestamp is still valid).
Would this be a good approach? Or is this too much? (Can there be too much security?) Or do you have any alternative solutions?
Or is this too much? (Can there be too much security?)
You're talking about it like security is a liquid that must fill-up a container without overflowing it. That's not how it works and you're asking the wrong questions, which means you're trying to solve the wrong problem. It has nothing to do with the amount of measures that you pile up, but whether and how they address a specific problem.
If the problem is protecting data in transit, then the solution is TLS (SSL) - that's what it is specifically designed for, and anything that you can come up with would, in the very best case scenario, be a poor alternative to it. You can't outsmart the decades of research and practice that has gone into TLS.
That was already answered by Jay Blanchard though ... I want to point out the mistakes you've made, because otherwise it looks like one man's word vs. another (and you may listen, but other readers might not):
SSL:
The communication between server and client should always be https:// - nevertheless I read several articles warning that SSL is "no silver bullet", but it is a good start.
It is both a silver bullet and not a silver bullet, depending on how you look at it.
When we talk about protecting data in transit, it is THE solution - a silver bullet in a way.
But that doesn't mean flaws won't be found in time, or that you just switch it On and say "I have TLS, I am secure!" - no, it still requires proper configuration, maintenance and adjustments over time. In that sense, it's not a silver bullet.
It also doesn't solve many other security issues, so when someone asks "How do I make my application secure?", of course you would say that it is not a silver bullet - many threats need to be separately addressed and there's no one-stop shop for them all.
Hash PW clientside (SHA3, ARGON2i, BCRYPT):
Many comments did reject hasing PW. Using a hash, comparing it to a HASHed PW in the database would simply change the PW from the userinput to the HASH - an attacker would still have access by simply getting hands on the HASH. I agree. BUT (this is what I meant that people read about different aspects of security) the ones claiming that it is better than sending plaintext because in that case ONLY your system, but not other systems with the same PW would be compromised (unless, of course they use also hashed PWs). So I would implement HASHing of the password before sending it via SSL.
It's the exact opposite - when you hash the password on the client side, that only makes accounts on your site easier to compromise after a data breach.
Lookup the hash in the database - got the password right there, that's the part you figured out. But that hash is still the result of a certain user-provided string ... Nothing stops an attacker from applying the same techniques to break the hash in order to compromise accounts on other servers.
So, this doesn't in any way solve the problem, but you'll probably think that in the worst case it doesn't do anything bad ... Well, indirectly it does - you have to make a considerable effort, to implement something with a lot of potential for mistakes.
In the best case scenario, you're just wasting your time, but a minor mistake can be a major vulnerability.
Also, SHA-3 is a cryptographic primitive - it has many appliances, but mostly as a building block. You can't just put one round of it on a password and be happy with the resulting hash.
For a comparison, bcrypt uses Blowfish internally (as a primitive of the same kind as SHA-3), but you cannot equate Blowfish to bcrypt.
encrypting the HASH:
Let's assume SSL could not hide the data we send to the server and an attacker would read the HASHed PW. The only way that I could think of to adjust the security to this scenario, would be to encrypt (e.g. AES CBC) the client-side HASHed PW with a key that has been send beforehand by the server, and that has a short expiration period. The key would have to be generated randomly. Like that, the server can decrypt the data, and then compare the HASH with the one it its database.
There are valid reasons to encrypt password hashes, but not for this purpose and certainly not on the client side.
You need a secure key-exchange protocol for this to even work. And guess how you do that? TLS.
There's literally nothing different between communicating an encryption key or a password over the wire. So, even if this was somehow a solution to protecting a password, how would you apply it again on the key itself? It makes no sense.
SSL is just fine, I have no idea why you disagree. Client-side hashing still leaves the PW visible on the client-side as well as the hash, so nothing gained there.
The question comes down to, "what are you protecting?" My guess is that you're not guarding anything needing more security than banks, and probably way less than that.
You're spending a lot of time trying to re-invent the wheel here, rather than relying on tried and true methods. Stick with what is proven.
-> Clients wants to Login via SSL -> Server sends back a key -> Clientside hashing of the PW -> clientside encryption of teh HASH with the key and a random IV -> Server decrypts the data with the key (stored in $_SESSION, with an expiration timestamp) and compares the HASH with the HASH in his DB (if the expiration timestamp is still valid).
Why encrypt a hash? That implies that the hash isn't secure enough. OK, fine, let's go with that. So let's assume an attacker is able to read the hash, and that's the reason you want to protect it with an additional layer. If an attacker is in a position to read the hash, they're also in a position to read the key sent by the server to the client, and the Javascript which contains the encryption algorithm (assuming you're talking about an HTML scenario here). Now the attacker has everything to replicate and reverse the encryption, and in fact they are probably also in a position to alter the Javascript sent from the server to the client in the first place.
To protect against that happening, you'd need some wrapper that protects all the communication between the client and the server, like, oh, uhm, say… SSL.
Since SSL already protects the communication from 3rd party interference… what do you think you're adding with that additional song and dance exactly? I'll tell you: nothing.
Related
I am working on a website to learn more about web programming and also to launch same as a start-up. The first problem that i came across was how to implement a secure login system. Currently i have take steps like escaping the password and then hashing it, using a salt. But i was wondering is the following mechanism secure,
I will make the user enter username and will keep checking if the user has entered his username (when textbox loses focus or a button for submitting username, also to prevent listing of usernames ill block the user by setting cookie on system if multiple incorrect attempts are made, or maybe ill use captcha for each ), once entered username, ill send back random stored salt to user.
Using that salt and password entered, user will hash the password and send it in form
I'll verify the password by comparing hashes
I think it will be beneficial since server side i don't have to do any processing, and hence i don't have to worry about DoS attack, as i read somewhere that using some slow hashing like BCrypt will expose site to DoS attacks.
Also user's password is never communicated over the network, making it secure from person sniffing the network.
Please do point me to some reference or anything that may help me to implement securely. Consider me noob because i have still started learning, and would like to know what you think of it, what possible flaws are there ? and what must be secure strategy.
UPDATE-
Many answers i am getting, all usually assume that i am thinking this as an alternative to SSL; no it's not the case. By protection against sniffing i meant protection just in case some attacker might make the user use a SSL proxy.
just for reference - https://security.stackexchange.com/questions/19616/why-is-it-possible-to-sniff-an-https-ssl-request
Client side hashing can have its advantages, but you cannot do without server side hashing. In your scenario, the calculated hash acts as the new password. An attacker with read access to the database (SQL-injection) will see this hash and can use it directly as password to login.
Using a slow hash with a cost factor is mandatory, usually it is done server-side, because client-side languages are slower and can do less rounds. Of course somebody can use it to make a DoS attack, but this can be done with every other page as well. The size of the password doesn't matter (as one can read ocassionally), because after the first round only the hash will be hashed.
If you plan to do a client-side hashing, don't forget to calculate a (fast) hash on the server as well. And you have to ensure, that the hashing was done correctly client-side. Much more important is, that you use SSL to send the credentials.
The question Secure authentication: partial client-side key stretching… could be of interest to you.
EDIT:
I will try to sum up the important points for client-side hashing.
A slow hashing algorithm with a salt and a cost factor (BCrypt/PBKDF2/SCrypt) is mandataory, this is the only thing that makes it hard to retrieve the original password from a hash, if the password is weak. It is possible to do this client-side.
Server-side hashing is mandatory too, to prevent an attacker from using stored hashes directly as passwords, if he knows them. The hash can be fast without a salt (SHA-256), because the input (BCrypt hash) has enough entropy. Such a strong "password" with 60 characters cannot be successfully brute-forced.
If the attacker cannot crack the fast SHA-256 hash because the input is too strong, he can try to brute-force with the original passwords (from a dictionary). But to do this he would first have to calculate the slow BCrypt hash and afterwards the fast SHA-256 hash.
Client-side languages like JavaScript are usually interpreted and much slower than compiled code, so you can do less rounds in the same time as you could do on the server (this weakens security). If you have the possibility to run native code on the client, there is no disadvantage to do the slow hash client side.
no, you should not send any 'salt' to the user.
It can be sniffed.
What you're basically doing is a send something like a (csrf-)token that can be used once. Nothing wrong with that, but you seem to be re-inventing the wheel.
Seriously, I think your solution is good only for hackers. If I sniff communication I will get gradually username, salt and password hash. You have to send all those values over the network (username to get salt, password hash to auth attempt). Now I can use sniffed password hash directly in malicious login request or start cracking password locally (users usually have same password for more services). All checks and limits on auth attempts are out of game because I don't need send request to guess password. Depends on hash algorithm it will more or less time consumption. I think network sniffing is the main purpose why you calculate with not sending plain password over the network.
You can secure your network communication with TLS but then all things with sending salt and hashing password on client are unneccessary. You can just send password in plain text to server. But yes, hashing password on client, why not if you want. You can use ie. sha1 also on server if you think that bcrypt is performance issue and possibly DOS cause.
Good example of protocol that is used for transmitting infomation through unsecure network is OAuth 1.0a and even in it you need some cryptography or TLS for transfering consumer secret.
Let me know if I am something understood incorrectly.
I guess only drawback i can see is using this on low-end mobile devices.
As my webserver doesn't allow use of the PHP_AUTH_USER and PHP_AUTH_PW keywords because of CGI (Col. Shrapnel in 'PHP_AUTH_USER not set?') I'm examining some other ways to send a user's password to the server.
Sending the password as plaintext using POST is a no go: capturing packets and looking at the header reveals the password.
So I could hash the password first. But someone intercepting the packet and copying the hash and username could still login using this information, right?
Making the hash dynamic by using a timestamp could prevent copying the hash. Sending the password as (timestamp + hash(password+timestamp)) (Last.FM uses something like this). The server could then subtract the timestamp (check if it's not expired or something), and hash the known password with it and check if they are the same. But then the password has to be known by the server, so the question remains:
how to get this password safely to the server upon registering?
Then, there's https, requiring a SSL certificate, which is not available for me (not worth the money (yet?)).
Any thoughts?
p.s. In the end I want to authenticate an Android app against my webserver
Where I've needed that level of security for data sent to the server I've used a similar system, hashing the data with a fixed-length client-derived key, appending, prepending or interleaving that key and converting the resulting string to hex. In theory the string could be decoded if intercepted, but realistically you'd need at least some idea of the schema used to derive the key.
If security really is so important to you, I'd really go for something like HTTPS. If it's not available to you, maybe switch webhost or whatever is your limitation? If money is the problem, there were some suggestions in the question comments (free ones, self-signed, etc).
This hashing with timestamp and such is pretty much only going to end up as security through obscurity (related question).
I'm not a security expert though. All I know is that security is very very hard, and the ones who want to break in are usually smarter than me. So I try to keep things simple and use common well-tested solutions instead of trying to come up with my own "clever" thing.
I'm trying to make a "normal" username/password login form secure, without needing HTTPS. My idea is this:
Server generates a keypair for some kind of assymetric encryption algorithm. It stores this keypair in a temporary table of sorts (or perhaps the local session data).
Server sends the form to the client and includes the public key.
User fills in the form.
Before it's sent to the server, Javascript encrypts the password using the given public key.
Form is sent.
Server decrypts the password with it's private key (which it gets from the temporary table, using the public key to find it).
What I need to know for this is:
Which encryption method is the best to use? RSA?
How can I decrypt the password in PHP?
And probably the most difficult one, how can I make Javascript encrypt the password?
In advance: I'm sorry for being negative, however;
Implementing your own security protocol is never a good idea, unless you're a highly trained security expert, or you actually don't really care about the security and only want to create an impression of security (marketing) and stop the script kiddies.
SSL is definitely not a fingerprint lock, as so say in your comments, JCryption and your proposal are equal to having a door where you can enter a two-digit code to open the door and you have infinite many retries. It's hard to break if you're not really interested and just passing by, but if you want to get in that house (and you probably do, else security wouldn't be needed), you will get in.
Another point is that people often forget to mention what they want to achieve. Security has the famous three components called CIA, namely confidentiality, integrity and availability. Is it for you important that the data you transport is confidential, or is integrity important (i.e. you're sure that the sent data comes from the one you expect and not the man in the middle)?
To make it concrete in this case, the only thing that you achieve here is that a passive attacker cannot see whats passing by on the line. As soon as your attacker gets active and changes the messages on their route, your whole security falls apart. So my advice would be to just stick with the solution the experts have come up with (TLS in this case, not ssl since that is the old version of it) and just make sure your server supports it.
edit:
Btw, SSL/TLS cannot work without certificates. The whole point in public key crypto is that there should be at least somewhere some trusted party.
On the other hand, if you don't care that your users will get an "invalid certificate" message, you can just create your own certificate which is really easy. In that case your certificate isn't trusted by the browsers, however, you can be sure that at least your communication is safe (okay, there are exceptions in this case, but still ...)
The argument that certificates should be for free is really from a perspective point of view. I think people who claim it is bogus/idiotic don't know what it takes to be a certification authority. These companies invest millions in order to keep the communication secure, and sure they make nice money out of selling certificates, but hey its their job and they also deserve to make money, just like any others.
edit2: after comments
I indeed say that you have a secure communication. However, you miss the point that with self signed certificates you dont know to whom you talk securely. Imagine a dark room which is completely isolated from eavesdropping a conversation. Now imagine the difference between such a room with and without light. If the room has light, you can actually see to whom you're talking securely and only choose to talk to people you like to trust. Now imagine doing the same in a completely dark room. You can only hope that the guy you talk to inside this dark room is just an ally and not your adversary. However, you cannot know that, just hope that it's ok. And although your conversation itself is secure, nobody can listen in, you still dont have "full" security.
If I, being a crook, do a man-in-the-middle attack, I can create a self signed certificate without the user noticing. So the advantage of using TLS with self signed certificates is that you have at least the implementation of the protocol corrent (and even implementing this is far from easy). Moreover you can avoid the ugly warnings by advising your users to manually trust the certificate once. However, this is only possible if you have a relatively small group of returning visitors, for a public website this is not really a solution.
This doesn't seem that secure from the perspective of the client. Two (related) problems:
How does the client trust the server? How can it verify that the key the sever's presenting is the one that belongs to it?
It's possible to do man-in-the-middle attacks. A malicious proxy could strip out and store the public key before the client sees it, substituting its own, then decrypt the password when the client authenticates, store it, and re-encrypt and send the response on so the client doesn't realise something's up.
What's wrong with ordinary SSL? There has to be a consensus that it's secure, otherwise vendors and organisations would drop support for it overnight. By contrast, most attempts to invent a funky new way to do security "on the cheap" usually miss something fundamental.
It looks like a lot of what you want to do is supplied by the jquery plugin JCryption. It even assumes PHP as the backend, so a good fit for you.
Livejournal does something similar to what you want where:
Server generates a challenge string, inserts this into form. [1]
Client generates response by MD5 hashing the password, then MD5 hashing the previous hash with the challenge prepended [2].
Server gets response, checks challenge validity, then does same as step 2, comparing the result to the response.
This is a very good idea, and it's already been done. See jCryption.
jCryption looks interesting, I've not seen it before.
But I have to ask what is wrong with SSL?
Encryption code is notoriously hard to do right, and you can bet that the SSL implementations found in browsers and http servers are much more rigorously tested and reviewed than the jCryption stuff.
That said, jCryption looks neat if you absolutely need to avoid SSL, and you're not dealing with super-sensitive information.
By storing the passwords in encrypted method on the server the server can retrieve the passwords and verify checksum sent by client. Send a session password and ask client to make a hash of session password and the user inputted password, do the same on the server and compare the two hashes.
This will not secure users from MITM attacks - local admins, NSA, telecom, router hijacks, but it will keep the password safe in open wlan.
How do I write/put together a secure login in PHP? The website developer guide said I shouldn't roll my own, so referring to samples available via Google is useless.
How do you pros do it? Lets say you're building a world-class app in rails, would the same libraries / techniques be usable here?
Thanks
In Rails, one would generally use a pre-existing library. Authentication is easy to do wrong, and the problem's been solved so many times that it's rarely worth the effort to solve it again. If you are interested in writing your own implementation, then I'll describe how modern authentication works.
The naive method of authenticating a user is to store their password in a database and compare it to the password the user submits. This is simple but unbelievably insecure. Anyone who can read your database can view anyone's password. Even if you put in database access controls, you (and your users) are vulnerable to anyone who hacks around them.
Proper form is to use a cryptographic hash function to process the password when it is chosen and then every time it is submitted. A good hash function is practically irreversible -- you can't take a hash and turn it back into a password. So when the user logs in, you take the submitted password, hash it, and compare it to the hash in the database. This way, you never store the password itself. On the downside, if the user forgets their password, you have to reset it rather than send it to them.
Even this, however, is vulnerable to certain attacks. If an attacker gets hold of your password hashes, and knows how you hash your passwords, then he can make a dictionary attack: he simply takes every word in the dictionary and hashes that word, keeping it with the original. This data structure is called a rainbow table. Then, if any of the dictionary word hashes match a password hash, the attacker can conclude that the password is the dictionary word that hashes to that password. In short, an attacker who can read your database can still log in to accounts with weak passwords.
The solution is that before a password is hashed, it is combined (usually concatenated or xor'd) with a value called the salt which is unique to each user. It may be randomly generated, or it may be an account creation timestamp or some such. Then, an attacker cannot use a rainbow table because every password is essentially hashed slightly differently; he would have to create a separate rainbow table for every single distinct salt (practically for each account), which would be prohibitively computationally expensive.
I will echo the advice of the other answerers: this is not simple stuff, and you don't need to do it because it's been done before, and if you do it yourself you stand a very good chance of making a mistake and inadvertently compromising your system's security. But if, for whatever reason, you really, really want to write one yourself, I hope that I have provided an (incomplete!) outline of how it's done.
The Zend Framework has an 'Auth' module which would be a good place to start. Or, if your site will be hosting an install of WordPress or PHPBB, there are ways of leveraging those technologies' authentication modules to sign in to other pages of a site.
One thing to look at when you are trying to authenticate is what is your real goal.
For example, on SO I use my google login, and that works, as they just need to know who I am, and they can trust that Google has an idea. So, if that model will work for you, then look at using OpenID, as there are various tools for that.
If you must do your own, then there will be various tests to ensure that it is secure, again, depending on how paranoid you want to be.
Never trust anything from the user, unless you have used some strict verification.
Use https to help protect the password of the user, you owe them that much.
I will end my response here as Thom did a fantastic response.
by Soulmerge:
I think the accepted answer in your other question states it pretty well. Hash the passwords with a salt. Other than that, there are some security ideas on the transport layer:
Use https when sending passwords. This makes sure nobody can catch them on the wire (man-in-the-middle attack or the client uses an evil proxy)
An alternative is to hash the password using javascript when the login form is submitted. This makes sure that the password is never transported in plaintext. You should hash the hashed value again with a salt on the server. (md5($_POST['postedPwHash'] . $salt))
a good method to somewhat secure the client-server transaction (if no ssl is available) is to use a one-time random key to create a unique hash from the credentials, then only send that unique hash to the server. the server then compares this hash to its own generated hash instead of comparing it to the real credentials. this would provide a good defense against the man-in-the-middle attack. the downside is that to do this the user must have JS enabled (at least i dont know of a good method to encrypt client-side data without it). this means that you will need a sufficient fallback when it isn't on. you can even create the form in JS to make sure its enabled.
this library is a simple library i wrote once that does the procedure i described, though it probably needs some improvements.
note that this is in addition to using "salting" methods and other server-side security measures. it is also quite vulnerable to dictionary attacks as the entire hashing process is by definition procedural, predictable and visible to the user (as JS always is).
My answer is "Don't do it"
This is a very complex area, full of potential security gotcha's. If you are not an expert in this field, then you are really just asking for trouble and problems down the road.
I would recommend looking at getting an existing solution to do. Sadly I don't know any that I would be happy to recommend, other than openid. I'm sure you will get some good suggestions here though...
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.