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

Machiavellian Monks,

Basic situation - an online user interface form asks the user for an ID# to sign in. The input ID# is checked against a MySQL DB, and if valid, the user is given access to another page for updating their personal information.

Here's what I've done for security:

This not a banking site or anything like that - I need "pretty good security", not CIA-proof security. So I have couple of questions:

  1. Does this sound reasonable?

  2. I'm aware of the problem with using crypt() on 20-character session-id's. Already noticed that if two session ID's are different by only the last few characters, I get the exact same encrypted value - what to do?
Thanks




Forget that fear of gravity,
Get a little savagery in your life.
  • Comment on Using crypt for 'reasonably' secure session management w/DB

Replies are listed 'Best First'.
Re: Using crypt for 'reasonably' secure session management w/DB
by kyle (Abbot) on Jan 29, 2008 at 16:36 UTC

    crypt is supposed to do what Digest::SHA1 actually does, so consider using that instead. It works equally well on strings of any length. If you want slightly shorter encrypted strings, see Digest::MD5, which is not quite as secure but probably still good enough for you.

    I don't see the need to encrypt the session ID. What is that supposed to protect you against? I can't tell from your description, but I suppose it's possible that there's some data encoded in the session string that you don't want the site operators to have access to.

    In any security problem, the question you should ask is what you're defending against. What's the attack? What scenario are you trying to prevent, and how does your measure prevent that?

Re: Using crypt for 'reasonably' secure session management w/DB
by CountZero (Bishop) on Jan 29, 2008 at 17:09 UTC
    Your session ID should just be a random number (I use a UUID from Data::UUID for it) which is the key to the session data in the DB. There is thus no need to encrypt the session ID. It does not make your site more secure.

    I see two possible "points of failure":

    1. Can a user access your database and "steal" a valid session ID? (this is solved by limiting access to your database to only your web-server / CGI-scripts)
    2. Can anyone intercept the data between the client's browser and your web-server and thus obtain a valid session ID? (this is solved by using HTTPS rather than HTTP as your protocol).

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Using crypt for 'reasonably' secure session management w/DB
by mr_mischief (Monsignor) on Jan 29, 2008 at 17:34 UTC
    In response to question #2, your digest algorithm could be chosen to work with longer strings. MD5 (Digest::MD5) is more suitable to long strings than is crypt(). MySQL even supports an md5() routine, but I believe it's not an SQL standard feature if that matters to you. If using MySQL 4.0.2 or newer (and you probably should be), you can use SHA-1 instead. There is also the password() routine which is how MySQL hashes passwords for MySQL user tables (and there's encrypt() which is the same as crypt() in Perl).

    As for question #1, that requires a little deeper probing. You do realize that having the unhashed version of the session ID is pretty much the same as having the hashed version under this scheme, right?

    You need SSL if you're actually trying to prevent session hijacking while passing a session ID back and forth. If you're using this scheme over a plaintext wire, you're essentially protecting the browser session from the people who have access to the database directly. It'd be easier to hijack the session by sniffing the unencrypted session ID from a plaintext IP stream than by getting it out of the database and brute-forcing it, but anyone who has full access to your database isn't concerned with hijacking a single session anyway.

    If you're not trying to eliminate session hijacking but just to shorten the window of opportunity, then you don't need to hash the session ID on the server. Picking a reasonably short time frame that the session is valid can do that. It would help if the session ID changed every time the user loaded a new page so that one intercepted or stored in a cache would be invalidated with every new action by your user. Hashing an ID that was stolen in plaintext and sent in plaintext will result in the same hashed value as a legitimately sent plaintext ID, so why slow your server with the hashing?

    You could get fancy and make a JavaScript front-end handle shared secrets. Instead of passing the password and session IDs across the wire to the web server, the user could enter the password into a prompt for the JavaScript app. Then, the page requests a random string from the server and either hashes the random string with the password as a salt or uses it as a shared key in a reversible encryption. Then, the encrypted or hashed version of the random string is sent to the server, which either decrypts it or hashes using the shared secret as a salt to find a match. Since the shared secret never passes between the client and server, it's down to a brute force attack against the hashing or encryption algorithm.

    The question of "good enough" security is largely up to your client or employer, even if they don't understand the technology. SSL is a good way to get some additional security that's already designed and implemented, and the annual cost of a signed certificate is generally cheaper than having the developer fret for a long time about it. It's definitely cheaper than a security incident that releases any kind of personal information about your client's clients, especially if you're talking about a banking or healthcare field even when transfer of money is not involved. If your form has personal information in it like date of birth, mother's maiden name, or a government identification number (think SSN in the US), then that's just asking to be an identity theft target even if the session isn't hijacked. That information needs to be encrypted.

    If by "personal information" you just mean screen name and "about me" information that's displayed on a forum anyway, then that's no big deal. If it gives the intruder a chance to change that information or to log in on the forum, that's somehting more serious. If it's something that you could use to get a credit card, then it's something anyone else could use to get a credit card in your name. That means that any time you have that information involved, you need good security.

    I'm not a professional security researcher, but I've read lots by them and I've been doing web sites and other types of Internet-facing servers for a decade. My three best general bits of advice are:

    1. When dealing with security, assume the worst about people, because someone out there is like that and it only takes one.
    2. Read Schneier and others on the topic before doing anything, and read security sites like Security Focus to see what mistakes others are fixing so you don't make those.
    3. Never assume securing your application isn't important just because the data isn't important. Nobody would make any money just from having hijacked a Perlmonks account, for example. However, they might land a freelance job from another member based on the account owner's credentials or might try to ruin another's relationships with the rest of the community.

    You're right that there's no point in going overboard for a page that doesn't need much security, but cheap and simple security like SSL shouldn't be flippantly passed over if it's a good idea to secure it at all. Never count on the information being passed being the cost of a breach, either, since much information can be used to springboard into other illicit (ad)ventures.

Re: Using crypt for 'reasonably' secure session management w/DB
by olus (Curate) on Jan 29, 2008 at 16:42 UTC

    In addition to kyle's suggestion, I would consider placing the session in a cookie instead of having it floating around in hidden form fields.

    Or give CGI::Session a try.

Re: Using crypt for 'reasonably' secure session management w/DB
by johngg (Canon) on Jan 29, 2008 at 20:04 UTC
    the script then generates a 20-character session ID, encrypts it using Perl's built-in crypt() function

    Already noticed that if two session ID's are different by only the last few characters, I get the exact same encrypted value

    Note that crypt will only handle passwords up to 8 characters long. I guess you could follow other Monks advice and use use something like Digest::SHA1. This code

    use strict; use warnings; my $salt = q{zy}; my $passwd = q{}; for ( q{a} .. q{m} ) { $passwd .= $_; my $encrypted = crypt $passwd, $salt; printf qq{%-3s%-15s%13s\n}, $salt, $passwd, $encrypted; }

    produces

    zy a zysdihJ0gvwVY zy ab zy4zYYneKaYuc zy abc zyVTopNmDAhDM zy abcd zyaO8uXTZHk.Q zy abcde zyIALca4k5yc2 zy abcdef zyTUr1c/J9ROc zy abcdefg zyB0MLdTVOSYY zy abcdefgh zyQskE0hBAgLs zy abcdefghi zyQskE0hBAgLs zy abcdefghij zyQskE0hBAgLs zy abcdefghijk zyQskE0hBAgLs zy abcdefghijkl zyQskE0hBAgLs zy abcdefghijklm zyQskE0hBAgLs

    which demonstrates that crypt just discards anything beyond the 8th character.

    I hope this is of interest.

    Cheers,

    JohnGG

Re: Using crypt for 'reasonably' secure session management w/DB
by samtregar (Abbot) on Jan 29, 2008 at 21:09 UTC
    I've gotta ask, why are you making your users enter IDs directly? Wouldn't a username and password be both more secure and easier to implement (you could just use standard auth and session modules, for example). Consider that by making your users enter a long number you're basically forcing them to write it down somewhere.

    -sam

Re: Using crypt for 'reasonably' secure session management w/DB
by Anonymous Monk on Jan 30, 2008 at 12:42 UTC

    You say that you "checks the referrer". You know that the HTTP Header "Referer" can be set to any arbitary value by the client? Do not trust any data sent from the client, including the "Referer" header.

    Encrypting the session ID is nonsense. It does not improve security. Just make sure you do not have predictable session IDs. Use long random values (or UUIDs).

    Alexander