Description: |
While implementing a generic POP3/SMTP Web-Interface (see a German language version at http://webmail.zf2.de) I thought about storing sensible user data (like passwords) over the session. After dropping my first idea to store that data on the server (which is possible but unwanted by our customers), I decided to store those passwords in the Cookie.
As it may be possible (in this case even most probably) that a user will use that interface from a foreign computer (maybe in an internet cafe) I needed to ensure that nobody else could read this information (from the cookie).
The final approach is:
- Encrypt the password with any Crypt::CBC enabled encryption scheme.
- Store the encrypted password in the Cookie together with a session id and other values (POP3 server et. al.)
- Store the encryption key together with the session id on the server.
- In short: the user get the cipher, we get the key.
I think this procedure is fine to ensure client security and some server security by not storing the password server-side. I know that the script itself knows the password, but I don't consider this too risky.
I would like to have some comments on this approach. Did I miss something? Did I oversize the problem? Please give me some feedback
alex pleiner <alex@zeitform.de>
zeitform Internet Dienste
|
use Crypt::CBC;
use CGI;
my $q = new CGI;
my $encryption_method = "Blowfish";
# get cookie
my ($login, $password, $pophost, $smtphost, $session) = getcookie();
## do something here such as fill template et. al.
# set cookie
my $cookie = setcookie($login, $password, $pophost, $smtphost, $sessio
+n);
print $q->header(-cookie=>$cookie, -expires => "now");
print $template->output;
############################################
sub setcookie {
############################################
my $login = shift;
my $password = shift;
my $pophost = shift;
my $smtphost = shift;
my $session = shift;
my $key = $ENV{UNIQUE_ID};
my $ciphertext;
my $cipher = new Crypt::CBC($key, $encryption_method);
$ciphertext = $cipher->encrypt($password);
open ID, ">$Conf::tmp_dir/$session" or print_error("write_error");
print ID $key;
close ID;
my $cookie = $q->cookie(
-name => "zf_webmail",
-value => {
pop3 => $pophost,
smtp => $smtphost,
login => $login,
password => $ciphertext,
id => $session,
},
-expires => '+10m');
return $cookie;
}
###########################################
sub getcookie {
###########################################
my %cookies = $q->cookie(-name => "zf_webmail");
my $login = $cookies{login};
unless ($login) { # no cookie -> no session
# print error
} else {
my $ciphertext = $cookies{password} or print_error("no_cook_passwo
+rd");
my $pophost = $cookies{pop3} or print_error("no_cook_pop3")
+;
my $smtphost = $cookies{smtp} or print_error("no_cook_smtp")
+;
my $session = $cookies{id} or print_error("no_cook_sessio
+n");
my $password;
open ID, "$Conf::tmp_dir/$session" or print_error("read_error");
my $key = <ID>;
close ID;
my $cipher = new Crypt::CBC($key, $encryption_method);
$password = $cipher->decrypt($ciphertext);
return ($login, $password, $pophost, $smtphost, $session);
}
}
Re: Encrypted Storage of sensible Data in a Cookie
by davis (Vicar) on Oct 22, 2001 at 13:58 UTC
|
You may also want to try a MAC (message authentication code),
whereby you generate a one-way hash (MD5 or similar) of the
contents of the cookie together with a "secret key", known
only to the server.
When you get the cookie back, you
compare the MAC the client hands back with a freshly generated one.
This is to ensure the client doesn't alter the cookie you hand
them. Chapter 6 of "Writing Apache Modules with Perl and C"
(O'Reilly) is probably useful.
It also recommends using an
MD5 hash of an MD5 hash of the data, for reasons I can't
remember. | [reply] |
|
| [reply] |
|
But then I have to store the password plaintext on the server, right? This is not what I wanted.
But thanks for the hint, I just started reading that book and will hopefully gain some insights.
alex pleiner <alex@zeitform.de>
zeitform Internet Dienste
| [reply] |
|
Almost. You're not storing the users' passwords on the server, but the "secret key".
Here's some actual (old) code:
my $secret_key = "BLAHBLAHBLAH";
my $session_cookie = $query->cookie('SessionID');
umask 0066;
if($session_cookie) {
my $mac;
if(($sessionid, $mac) = split("-", $session_cookie)) {
###Ok, the user has returned a cookie,
###let's make sure it's not been tampered with
+.
if($mac ne md5_hex($sessionid . md5_hex($sessi
+onid.$secret_key))) {
destroy_cookie($sessionid, "MODIFIED")
+; ###Ack. Nasty people
return;
} else {
###Other checks.
}
This way you're not storing the password, you're just making
sure the user doesn't modify the data. A reasonable golden rule is: "NEVER trust the data the user hands you".
| [reply] [d/l] |
|
|