Perl and Cookies
htmanning
created: 2006-03-05 22:21:17
Hi, This has to do with cookies, but I thought someone here could help. I have a perl script that has people submit user/pass into a sql database, and then sets a cookie as follows:
sub write_cookies { 

    print "Set-Cookie: reguser=$username; expires=$expires; path=/;\n";
    print "Set-Cookie: regpass=$password; expires=$expires; path=/;\n";
    print "Set-Cookie: regtype=$regtype; expires=$expires; path=/;\n";
}
So it prints a username, password, and regtype (regtype tells whether they're a member or not). So then I have the following code on pages that I only want members to see:

This works if I only use this script. The scripts checks the database and only let's people in that are registered, and presents a login screen to others. My problem is that I'm also running an online photo album software that sets a 4th entry into my cookie. In IE, my code still works but in Firefox I can only get it to work by changing the 3 to a 4 in this line in the javascript:
var the_regtype = broken_cookie[3];
changes to:
var the_regtype = broken_cookie[4];
If I change the 3 to a 4, it works in Firefox but not in IE. Is there a way around this? Is this a Firefox bug? If I delete the photo script's cookie my script works fine. Any insight would be appreciated.

On another note. How secure is this type of security? If people can login once, view the source of my HTML and get the javascript code, could they conceivably build their own cookie to get around my script? Thanks. Tom

Re: Perl and Cookies
created: 2006-03-05 22:44:01
I'm not sure, but I would guess that HTTP provides no guarantees about the order of cookies. The workaround for this would be to search the array for the right cookie, but it's not worth implementing, because...
On another note. How secure is this type of security?
Not at all. It would be extremely easy to work around this security entirely. It looks like your example could be worked around by simply disabling JavaScript. The problem is that by using JavaScript, you're trusting the client to do the authentication, and ultimately the client is under the control of the user. You really need to do your authentication on the server, which is (hopefully) under your control.
Re^2: Perl and Cookies
created: 2006-03-05 23:49:01
Wow, you're right. Do you think I could embed some perl script that would be called when the page is loaded? That script could read the cookie and then set a variable yes or no? Any snippets you could give me? Thanks.
Re^3: Perl and Cookies
created: 2006-03-06 08:51:58
The easiest way to do this is to use the authentication built into your HTTP server. Search for "HTTP Basic Auth" in your documentation, and you should find a few commands to password-protect pages. The second easiest way is to write a small CGI script to check the password and only return the page if the correct password is given (make sure it only returns the appropriate pages, and can't be fooled with things like ../../../../../../etc/passwd). You can also do some pretty cool magic with mod_perl and its authentication handlers with Apache::AuthCookie.
Re: Perl and Cookies
created: 2006-03-05 23:13:05

As sgifford points out, it's insecure. You might be able to do something like create a cookie that has no information other than a random, unique ID number. Fetching the cookie, you could then check it against information kept in your database, to ensure that this user is allowed access. Doing this would prevent the sensitive information from being seen.

Incidentally, I recommend storing the password in an encrypted form. To check password validity, put the login password through the same encryption and compare with the stored value.

Re: Perl and Cookies
created: 2006-03-06 01:31:37
You can set a single cookie with the username and password, delimited somehow, and parse it yourself. Or use something like CGI::Cookie.

BUT

Just to drive it home:

  • Do all authentication on the SERVER side.
  • Don't SEND any content you don't want being seen.
  • Don't send the username, and especially not the password, back and forth in plain text for every transaction. Use a session ID to identify the user (like a temporary username, auto-gen one that's unlikely to be guessed) and a MAC as a temporary "password." Use a one-way encryption of the session ID (something like Digest::SHA) salted with a secret string.
    Then just validate the MAC by comparing it to the re-encrypted session ID provided to decide if they've logged in.
    Re^2: Perl and Cookies
    created: 2006-03-06 04:01:28
    Yes, I agree with previous recommendations, but I want to describe my way of session ID creation: I have the stored procedure in my db (PostgreSQL), which generates random text with length specified by argument and check it's uniqueness against proper column of the session table. The first character could be a..z or A..Z, the others could be 0..9 too. I guess that 10 characters is enough... No secret salt, no digest, but slightly slower when new session ID created.
    Re^3: Perl and Cookies
    created: 2006-03-06 14:12:16

    That's not a great way to do session IDs. There are a few reasonably reliable ways:

    1. Take a hash of the username, IP address, and date/time of login. This should be unique as long as no one is cheating. ;-)
    2. Use a *sequential* ID in your sessions table. Hash this with the info above and it *will* be unique.
    3. Just use Data::GUID and store the Globally Unique ID (GUID) it generates as your session key. This is, actually, the simplest in my experience. It also guarantees uniquness and avoids the odd chance of hash collisions and such that the former two risk.

    In either case, save only the session ID in a cookie (or, if you prefer {and are willing to do a little extra work}, you can pass it in the URL and not use cookies at all). In the sessions table, store an expiration time; each time you check for a valid session, you can update the expiration (unless, of course, the session has already expired).

    This seems to be the best approach short of implementing some kind of full-featured auth scheme on the server side.

    <-radiant.matrix->
    A collection of thoughts and links from the minds of geeks
    The Code that can be seen is not the true Code
    I haven't found a problem yet that can't be solved by a well-placed trebuchet
    Re^4: Perl and Cookies
    created: 2006-03-06 17:20:07
    You'll re-compile the session id to check validity each time, right? You'd need 3 things: username, IP (granted), and login time(granted if stored for use as salt).
    IP and a request-incremented value are good additions- salt to taste.

    But if you only send the session ID, where is the username coming from?

    Re^4: Perl and Cookies
    created: 2006-03-07 04:27:20
    ad 1/ Can the user establish two sessions at the same time?
    ad 2/ Yes, I'v simplified my table - it has sequential primary key (aka ID) and the session identifier, I named it "token".
    ad 3/ I prefer simple solutions, and when I am able to fully satisfy the requirement with 50 rows of code, I am not using the modules, having thousands rows, potentially with bugs.
    You wrote that my way is not the great, but you did not write why :>))
    Re^5: Perl and Cookies
    created: 2006-03-07 09:57:23

    Can the user establish two sessions at the same time?

    That's up to you. ;-) Nothing prevents it, though such things always take a bit of thought to do well.

    I prefer simple solutions, and when I am able to fully satisfy the requirement with 50 rows of code, I am not using the modules, having thousands rows, potentially with bugs.

    You're... not using modules? Because they might have bugs? I'm willing to bet that the mainstream modules suggested in this conversation have a better test suite than your code, and the advantage of hundreds or thousands of testers. Sorry to be blunt, but that's a really stupid reason to avoid CPAN.

    You wrote that my way is not the great, but you did not write why

    Because you were randomly generating data, then checking for collisions. The busier your site becomes under those conditions, the more likely a collision, meaning more re-checks. Your site becomes slower at a non-linear rate -- thus, bad idea.

    <-radiant.matrix->
    A collection of thoughts and links from the minds of geeks
    The Code that can be seen is not the true Code
    I haven't found a problem yet that can't be solved by a well-placed trebuchet
    Re^6: Perl and Cookies
    created: 2006-03-07 11:03:35
    No, that's up to you, when you construct sess_id from user name, time or such stuff...
    I'v never wrote that I don't use CPAN, and if you want, you can see my preference of short own code rather than some module stupid, I see the same as independancy.
    The probability of collisions when my id's are generated is, I know, but it is theoretical when more than 10 character length used. But if you check it against sessions in db when created, your action is the same as when checking the same session id given from the next request of the logged user. I cannot see relevant slowdown...
    I know that I am not perfect and my solutions really contains bugs, but somebody can be interested in.

perlmonks.org content © perlmonks.org and blogical, htmanning, pajout, radiantmatrix, sgifford, spiritway

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03