htmanning has asked for the wisdom of the Perl Monks concerning the following question:

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:
<script language=javascript> { var the_cookie = document.cookie; var the_cookie = unescape(the_cookie); var broken_cookie = the_cookie.split("="); var the_regtype = broken_cookie[3]; if (the_regtype != 'member') { window.location="http://www.domain.com/cgi-bin/login.pl?logout +=yes"; } } </script>
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

Considered by acid06: mark as off-topic
Unconsidered by GrandFather: keep 11, edit 13 votes indecisive;

Replies are listed 'Best First'.
Re: Perl and Cookies
by sgifford (Prior) on Mar 06, 2006 at 03:44 UTC
    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.
      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.
        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
by spiritway (Vicar) on Mar 06, 2006 at 04:13 UTC

    As [id://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
by blogical (Pilgrim) on Mar 06, 2006 at 06:31 UTC
    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.
      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 checks 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.

        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