•Re: Is this a secure way to prevent cookie tampering
by merlyn (Sage) on Jun 29, 2004 at 14:36 UTC
|
Stop putting data inside a cookie. Use a cookie to brand a browser, and then use the branding to key into a server-side database. Given that DBM::Deep is pure Perl, even hosted solutions can access decent structured Perl data these days.
As long as your branding ID is chosen as cryptographically strong,
the only thing you have to worry about is a sniffed hijacked session. If
you're worried about that, simply use https protocol.
| [reply] |
|
| [reply] [d/l] |
|
| [reply] |
|
Thats the point, stash the sensitive information on the server side and use the session ID as they key to retrieve it. The actions should look like this: User connects to application, app auths user, on successful auth app gets a list of all access levels the user has permission to use. The info is cached into the session. only the session ID is sent back to the user's web browser. on future requests from the user, your app takes the Session ID from the cookie and retrieves the auth/access info from the local (server side) session store (keyed off the session ID). get it now?
| [reply] |
|
I, personally, prefer a hybrid solution. First, guessing of keys will always be possible for a user who can ascertain their length. The possibility of them guessing correctly is low, but as you begin to use up possible keys it will inrease. In solutions I use, I tend to have honeypot sessions, unused, that if accessed ban a user temporarily. That takes care of random guessers who try to bruteforce.
I also log at a basic level users attempts to login (by ip). It's my responsibility as an administrator to decide if a user is attempting to hack the site, or is having genuine problems.
Furthermore, I _do_ use two session IDs. One of those session IDs is only used securely, though, by setting the cookie to encrypted. That session ID is used for higher level authentication procedures.
Even moreso, for anything that involves money, or purchases I require users to input their passwords if their session wasn't created recently.
That keeps sessions pretty damn secure.
| [reply] |
Tamper-proofing vs. encryption
by gaal (Parson) on Jun 29, 2004 at 14:31 UTC
|
Apart from protecting yourself from spoofing, do you need to encrypt the data inside the cookie?
If you're just looking for a way to make a tamper-proof ticket, you can send the ticket data in the clear, plus a MAC (message authentication code). One advantage is that if you update the structure of the ticket, you don't need to change the decryption routine: there *is* no decryption routine. You receive a ticket, check that it is valid, and trust everything in it. (Actually, you could refactor your code to allow this in your approach as well.)
Also, in a real-world case you'll probably want to add an issue timestamp so that you can expire old tickets quickly — in the cleartext version, even before you waste CPU on crypto.
| [reply] |
|
Thank you. I will look into just using Digest::EMAC instead of all the encryption. I suppose the encryption was just used to ensure that the checksum was not tampered with, which the MAC would achieve.
--tidiness is the memory loss of environmental mnemonics
| [reply] [d/l] |
Re: Is this a secure way to prevent cookie tampering
by hardburn (Abbot) on Jun 29, 2004 at 14:43 UTC
|
No.
A cookie should store only a randomly-generated unique ID. You store that unique ID in a database somewhere, and everything else about the session is stored there.
----
send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.
| [reply] |
|
A cookie should store only a randomly-generated unique ID
Are you suggesting that I should rely on the randomness of the ID to prevent spoofing. Surely storing a serial ID and also a secret unique to the session would be better.
There are also issues with your approach such as the complexity of checking that the ID is unique and also generating unique numbers when the available pool is largely used. Admittedly these would not be issues for low traffic but they do exist.
--tidiness is the memory loss of environmental mnemonics
| [reply] [d/l] |
|
Are you suggesting that I should rely on the randomness of the ID to prevent spoofing.
Yes. If it is truely random, you don't have to worry about it. It won't stop replay attacks, but nothing besides strong encryption of the entire session will (such as HTTPS).
. . . complexity of checking that the ID is unique . . .
This is as easy as telling your database that the session ID field must be unique. A decent database will do the rest for you.
. . . generating unique numbers . . .
Random number generation is a hard problem, but not unsolveable. You benifit from the fact that a lot of people have already studied the problem and implemented solutions. My prefered way is to get some ammount of data from a cryptographically-secure random number generator (like /dev/random) and then put it through a hash (SHA1 prefered; pretend MD5 doesn't exist). SHA1 will then give you a 160-bit value which you store in your database and the user's cookie. That gives you 2**80 possibilities before you can expect keys to collide (due to the Birthday Problem). If you get one visitor per second, you can expect this to happen sometime around the time when the universe can't hold itself together anymore because all the usable energy has been converted to heat. (Of course, this assumes a good RNG).
----
send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.
| [reply] |
|
|
|
|
Re: Is this a secure way to prevent cookie tampering
by Anonymous Monk on Jun 29, 2004 at 16:43 UTC
|
Several people have pointed out that it's better to store an opaque id in the cookie, and they are right. However, the cryptography is also somewhat interesting. Your approach is vulnerable to a bit-flipping attack, which lets someone make certain modifications to the cookie even if they can't decrypt it. This is an inherent problem with CBC mode, and Crypt::CBC makes it easily exploitable.
use Crypt::CBC;
my $cbc = Crypt::CBC->new("Blowfish");
my $msg = $cbc->encrypt("foo");
print $cbc->decrypt($msg), "\n";
my $msg2 = $msg ^ (("\0" x 8) . "\4");
print $cbc->decrypt($msg2), "\n";
__OUTPUT__
foo
boo
Notice that I XORed the IV embedded in the ciphertext with 4, and that resulted in the decrypted plaintext being XORed with 4 as well. Combine this with Tweaking CRCs and you can undetectably alter the first few bytes of the cookie.
I second the recommendation for proper MACs like Digest::EMAC or Digest::HMAC. You can easily get bitten if you try to cook up your own ad-hoc scheme with CRC. | [reply] [d/l] |
Re: Is this a secure way to prevent cookie tampering
by Jeppe (Monk) on Jun 30, 2004 at 11:21 UTC
|
I go with the rest - only store a sufficiently large and random number in the cookie, and store the rest on your server. (That also adds the ability to update the data structures in the cookies).
However - if you're really concerned about security, remember to use https. That will prevent whoever's eavesdropping from catching your cookie! (This has become much easier since WLAN became popular and "easy-to-use") | [reply] |
Re: Is this a secure way to prevent cookie tampering
by exussum0 (Vicar) on Jun 30, 2004 at 13:02 UTC
|
If you need to store any information in the cookie, because doing db lookups for same tiny pieces of information, such as a username, user id or some sorta preference.
1. Encrypt it.
2. bin2hex/urlencode/uuencode it.
3. take the md5 and append it.
If the md5 of the encrypted info doesn't match the md5 of the encrypted/md5 part, you know someone was tampering with it.
Yes, you can do a lot of caching tricks server side, but sometimes, you don't have a choice.
-s
Bart: God, Schmod. I want my monkey-man.
| [reply] |
|
| [reply] |
|
md5 the plain text and then encrypt that. if you are worried about plaintext attacks, gzip the plain text to turn it to binary first.
TIMTOWTDI.
Bart: God, Schmod. I want my monkey-man.
| [reply] |
|
|
|
Re: Is this a secure way to prevent cookie tampering
by jayrom (Pilgrim) on Jun 30, 2004 at 14:23 UTC
|
I agree with hardburn.
What I use is taken from Lincoln Stein's Apache Modules book.
I create a string which is passed to the cookie and is also saved in a database for comparison.
It might be overkill but the cookie string is updated on each page as one of the fields used to create it is a timestamp. As Lincoln points out this is extremely sensitive to the smallest change in passed parameters, therefore is very hard to spoof and (almost) insures randomness.
use MD5;
my $MAC = MD5->hexhash( $secret.
MD5->hexhash(join '', $secret, @fields)
);
The $secret variable holds a 128 character string, which should be as random as possible.
The @fields array holds whatever data you want to use, as stated before preferably not relevant user data, which combination should of course be unique for each session.
Changing the $secret string on a regular basis will also provide peace of mind ;-)
Update
use MD5;
my $MAC = MD5->hexhash( $secret.
MD5->hexhash(join ':', $secret, @fields)
);
It looks like the book had a typo as this correct version of the code appears somewhere else in the book.
Sorry Lincoln, my bad!
jayrom
| [reply] [d/l] [select] |
|
@fields1 = ( "foobar", "baz" );
@fields2 = ( "foo", "barbaz" );
| [reply] [d/l] |
|
Very good point!
Proves that you should never trust code even from the accepted gurus.
Shame on me ;-)
jayrom
| [reply] |