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

Pals,

I'm rather fond of using long random strings as session ids, something like "Fa4ADX4dhd6f0ss7d6fkjl". These are generated by a mod_perl'd script, and are 20 alphanumeric, mixed capital characters.

I don't MD5 hash them, because they have no value except to identify the user as unique in a cookie I set.

I've heard in the past somewhere that this isn't a good idea. Can someone explain to me why? I'm a perl hacker, not a cryptographer, so please keep that in mind in your explanation.

I generate the session ids infrequently enough (e.g. only when a user logs in) and I'm not opposed to using a more processor intensive method to create them, if there's solid reasons to do so.

Thanks in advance!

-Any sufficiently advanced technology is
indistinguishable from doubletalk.

Replies are listed 'Best First'.
Re: Secure Session ID values
by Masem (Monsignor) on Nov 20, 2001 at 20:40 UTC
    At some point, you *will* hit a duplicated session ID, even if you only generate them infrequently. Sure, the chance is very very low, but it's still there.

    If you are generated unique ID for session management, a good way to guarentee is to use a combination of random characters, the time(), some counter (as to handle the very rare cases when two new sessions are started at the same time()), and possibly but not necessary some identify from the user ( but do not rely solely on this piece of info for user identification ). You should also hash this as to have some checksum characters in there to make sure that the key isn't randomly being guessed at. With the combination of time() and a counter, you can guarentee that every session ID you generate is unique (or at least until the 32-bit clock wraps around... :-).

    Of course, the other option, if you don't want to rewrite your code, is that upon generation of your completely random key, store that away in a database, with a frequent purging of keys that are no longer in use (say, after a few hours after being created). When you create another new key, just check to make sure that it doesn't exist already, and if it does, just generate a new one and recheck.

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

      I do a duplicate check (though I'm more likely to hit the lottery (and I don't even play it) than hit a duplicate key) and I delete the keys every day with a cron job. I also set the cookies to expire when the browser session ends (though you can get around this with LWP).

      Is linux/perl good enough at generating random numbers that this scheme I have is secure? Can someone, somehow predict future keys?

      I don't mind rewriting my code, if it's necessary.

      -Any sufficiently advanced technology is
      indistinguishable from doubletalk.

Re: Secure Session ID values
by mortis (Pilgrim) on Nov 20, 2001 at 20:58 UTC
    Picking something that generates unique values is very importiant. If your application ends up being distributed across multiple web servers, will your session id generator still generate provably unique ids?

    One security aspect revolves around issues of session hijacking. If an attacker can repeatedly request new session ids untill they figure out how the generator works, they may be able to predict valid session ids, and use them to access (hijack) sessions of your active users. For an ecommerce site, this can be a serious issue. Generating a duplicate id can lead to 2 users sharing the same session - possibly exposing sensitive information from one user to the other.

    There is a discussion about some of this in the book Writing Apache Modules with Perl and C.

    Some techniues that I've seen used are to embed the user's ip address in the session id, and to append an md5 sum computed from the session id plus some information on the server (that is only available on the server). The appended md5 sum can then be used to validate that the id was generated by your website, and the ip address can be validated against the remote user's ip. That simple validation goes a long way toward thwarting session hijacking.

Re: Secure Session ID values
by perrin (Chancellor) on Nov 20, 2001 at 20:35 UTC
    You can use anything that is guaranteed to be unique (I like mod_unique_id) as long as you secure it with some kind of MAC, as described here.

      Well, generating a string of 36 random alphanumeric characters SOUNDS pretty unique to me. (36^20=1.3e+31 possible combinations)

      I know how to use MD5 and other hashing algorithms, and I've heard it's a good thing to do so for session ids, but my question is why? Is generating the IDs as I describe above not good enough?

      -Any sufficiently advanced technology is
      indistinguishable from doubletalk.

(ichimunki) Re: Secure Session ID values
by ichimunki (Priest) on Nov 20, 2001 at 22:53 UTC
    You've gotten some great ideas so far in this thread. But really it is going to be next to impossible for an attacker to predict your session IDs-- especially if they are tied to another piece of information, like user ID.

    There is no such thing as perfect security. What you have to do is manage risks in a meaningful way. In this case, you've automatically expired the IDs, they are (presumably) tied to a specific login, and you're checking to ensure that you are not issuing a duplicate.

    The real question isn't "can an attacker figure out the next one"? The real question is, "can an attacker try all the possible keys in a useful amount of time?". If I can submit requests to your server with a valid login, and a "test" key fast enough, I might stumble upon one that is "open" (that is, in the valid list on the server) by sheer force.

    The basic dictionary crack is still one of the biggest vulnerabilities in any online system because the attacker can automate the attack and do it at a distance. However, if the target moves faster (thanks again to automation) than I can execute my dictionary crack then the odds of getting a hit by chance are a lot worse. If you're looking to enhance security, either make the key a *lot* longer (maybe something like 256 random characters-- if it's in a cookie this really isn't going to create that much more overhead-- and still, to mess with predictability, I'd salt with the time() at authentication, since then you're not relying solely on the random number generator for unpredictability) or add a feature that disables a login when that login attempts more than X number of accesses with an invalid key.

    FWIW, key length is the basic principle protecting things like PGP or other PKI schemes-- not that they've stumbled on some magical formula that makes numbers indecipherable to those who aren't supposed to look at them. The keys are huge (added: and yes, they interact in interesting ways to cause PKI, but it all comes down to key length). I've seen estimates are that it would take all of the computers currently on the planet working until the sun burns out to brute force crack some of the longer keys in use. Additionally they've thrown in the "secret passphrase", but that's mostly to make it harder to use a secret key even if it's compromised (that is, you've found my key-bearing floppy or CD, but unless you know my passphrase you still can't use my key), it doesn't make the keys more or less strong in the brute force arena.
Re: Secure Session ID values
by skyhook (Initiate) on Nov 20, 2001 at 21:47 UTC
    I often create a session key with MD5 or Digest::SHA1. Into it I feed a string of concatenated system values, usually including time(), $$, username if it's a system where I have this, and I intersperse rand values in there.

    I also add to that string a string of secret text that's either hardcoded or pulled from a file statically so that the SHA isnt being generated from a 100% dynamic bit of text that someone reading my code could figure out how to pattern analyze my sessionid's and predict one.

    Something akin to...

    use Digest::SHA1; my $sessID = sha1(time . rand . $$ . rand . $username . rand . $secret +bits);

    Now, I'm no cryptographer, and I don't play one on TV. I'm sure Bruce Schneier would look at that and laugh.

    It may be overkill, but when it comes to security I like to err on the side of paranoia. If I was just tracking sessions to customize someone's text, there's no real risk in hijacking. My sessions, however, are usually tracking someone to control access to hidden sections of a site.

Re: Secure Session ID values
by uwevoelker (Pilgrim) on Nov 20, 2001 at 21:04 UTC
    I always use time().$$ as session ID. It should be quite unique. If you want a fixed lenght use sprintf to format the two numbers.
      Certainly for unix-only apps this is a good choice for a session ID expect in really extenuating circumstances, but like others have suggested, you should combine it with a checksum. The uniqueness of time().$$ is good, but as with any user-supplied data (in this case, the users browser supplies it back to the server) you need to add something so that a malicious remote user can't fake the session of another user. A vaguely common (though not bulletproof) method would be to combine the two values with a 3rd secret value, in some mathematical way (eg ($$ * $secret) + $time) then take the modulus of that and another secret.

      Something like $checksum = (($$ * $secret) + $time) % $someprime

      When you need to access a users session, take their $$ and $time from the cookie, do the same maths on them, and verify that the calculated checksum is the same as the one in the users cookie.

      the hatter

        I also do this:
        I have a mysql-table with id (autoincrement), random, time, user and so on. When an user gets a new session I create a random number and the user gets the random number together with the id back. So if he alters the id or random, the session entry can not found in the database. The field time is for timeout and user stores the user id (or whatever you want).

        To delete outdated session I do: "DELETE FROM session WHERE time < $time" and $time is time()-$expire (expiration time in seconds).

      ugh. being unique isn't necessarily secure though. i mean, this is a fairly predictable number (in comparison to, say, the md5 sum of time().$$ encrypted with your pgp key). this wouldn't be that difficult to brute force, and if there was something valuable on the other end (money, classified info), then i'm sure someone would try.

      i reccomend this paper. This guy's perl isn't that great, but the ideas expressed are good, and there's several examples of hijacking session id's in the real world.



      BlueLines

      Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.