http://www.perlmonks.org?node_id=195717

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

I'm trying to decide on a session management technique. There are so many PMs out there that I don't know what would be best to use. Once users are logged in, they should be able to browse the site without re-entering their username/password (unless inactive for a period of time). I know that normally you wouldn't want to pass the username/password between the client and server as plain text, but this site will be running SSL. Does this mean I shouldn't worry about passing the username/password between the browser and server? I don't really need to track anything other than the username/password. This isn't a shopping cart or anything like that. It is however important that it is very secure. Any suggestions would be greatly appreciated. Thanks, Jeremy

Replies are listed 'Best First'.
Re: Secure Session Management
by valdez (Monsignor) on Sep 06, 2002 at 18:10 UTC

    If you can use mod_perl, then try with Apache::AuthCookie; module's documentation is very clear about its benefits, it says:

    1. The client doesn't *have* to pass the user credentials on every subsequent access. If you're using passwords, this means that the password can be sent on the first request only, and subsequent requests don't need to send this (potentially sensitive) information. This is known as "ticket-based" authentication.
    2. When you determine that the client should stop using the credentials/session key, the server can tell the client to delete the cookie. Letting users "log out" is a notoriously impossible-to-solve problem of AuthBasic.
    3. AuthBasic dialog boxes are ugly. You can design your own HTML login forms when you use AuthCookie.

    I found it quite interesting :) But this solves only half of your problem: you need also session management, right? Apache::Session could be an answer, but you may need to put some glue between these two modules.

    Ciao, Valerio

      I'm not sure I understand this bit

      When you determine that the client should stop using the credentials/session key, the server can tell the client to delete the cookie. Letting users "log out" is a notoriously impossible-to-solve problem of AuthBasic.

      Isn't the usual reason a server decides to 'log a client off' because of session timout after 20 mins or so? This normally occurs when they have simply moved on to another site or disconnected without informing the server they are doing so... in which case, there is not client to send the instruction to delete the cookie.

      Also, how does the server 'tell the client'? I know there is such a thing as 'server push', but I didn't think this had ever made much of an impact. Besides...

      The other way the timeout can occur is if the user is reading something long. If I was doing this and the server suddenly decided to 'send' me a 'your logged off' page, I don't think I would use that site again in a hurry.

      I think anything that relies on cookies is gonna limit your audience severely, and more so as new users become aware of the potential for misuse of cookies. The latest browsers that allow cookie control on a site-by-site basis mitigate this problem somewhat, but that requires users knowledgable to distinguish between sites they can trust and those they cannot. Unfortunately, things like the Trust-E symbol aren't worth the band-width that it takes to transmit them, as very little verification is done. ANd what verification is done, is done against the site "privacy policy", which invariably can be paraphrased as:

      <rant>

      {20k omitted}...of course, we won't use your private details for anything bad, but if we can find a way of screwing some commercial gain by using them to promote some aspect our, or our partners (Read:anyone who will pay us) businesses to you, we will! And by having said this here, hidden on another page linked from every page, in totally oblique, long-winded, legal terminology and frustratingly tiny print, we've effectively covered our arses should you ever discover what we are doing.

      </rant>


      Well It's better than the Abottoire, but Yorkshire!

        Your worries about compatibility can be solved using different solutions to the same problem. If you don't want cookies, then just use something like Apache::SessionManager, which can handle sessions inside URIs.

        Of course, this doesn't resolve privacy issues, as you can imagine, because it is still possible to track a user even without cookies. On the other hand, it would be quite impossible to put something in a basket on Amazon or build something like PMonks, without something holding a state. So there is no escape, except honesty.

        BTW, it is possible to expire AuthBasic accounts. Using cookies, just send an empty cookie (delete it) and the user will not enter your site without re-authentication. Under AuthBasic, just disable an account inside your htpasswd file. But you will need some more glue :-))

        Ciao, Valerio

        Also, how does the server 'tell the client'? I know there is such a thing as 'server push', but I didn't think this had ever made much of an impact.

        The server "tells" the client (to delete the session cookie) in response to a request from the client. In the timeout case, the server can also refuse to handle the request.

        Push isn't needed. If a web application really, really needs to know on a minute-by-minute basis whether the server thinks the client is logged in, use a hidden frame that refreshes on a periodic basis, and expire the session cookie on refresh. You can also include a javascript snippet to communicate "I've been logged out" to some non-hidden frame element.

Re: Secure Session Management
by barrd (Canon) on Sep 06, 2002 at 18:15 UTC
    Hi glickjd,
    The most secure system I have used for this type of scenario is a piece of OS software by the name of Interchange. It is touted as predominately an eCommerce solution but is actually a very good 'content management' system with eComm facilities built in. Supports either flat file or SQL Db management for the backend and utilises session management by creating a flat file filehandle for storing any arbitrary data that is desired. It may be a little OTT for your application but I would advise a quick look anyway as you may find some of the features to your liking. My main reason for suggesting this S/W is that is incredibly secure and written in Perl... YMMV.

    Update: typos fixed.

Re: Secure Session Management
by hacker (Priest) on Sep 07, 2002 at 13:13 UTC
    There's actually a third way that nobody has discussed yet, and in fact, it's the only way to do sessions properly, and in a secure fashion without depositing any insecure information that can be misused on the user's computer: Hidden Fields.

    Before you jump on and flame me, here's why: Hidden Fields do not leave "droppings" on the user's computer in any location (well ok, in their browser cache, but it expires once the page is reached anyway, if built properly). It also does not leave any bits in the URI for someone parsing their referer logs to go back into. I've actually managed to parse my own weblogs, caught an ugly referer, followed it back, and was into someone else's online mailbox. Not cool.

    Putting the session_id into the URI may fail in a lot of situations, such as with users behind a caching proxy. In many countries this mode of operation is essential due to saturation of international network connections.

    In my case, my users are very specific about what they will and will not allow on their systems.

    1. No cookies, at all, of any type
    2. No hashing or unique session identifiers in the URI
    3. Nothing left on their system when they leave my site

    It tends to be more of a pain than the other methods, but I gain more happy users (most of them are in Europe, which tends to take a very suspiscious tack with US websites and methods lately), and I get the benefit of their input on things like reporting bugs in the bugtracker, using the cvs, and so on.

    A couple of basic rules apply to any session management you build:

    1. No client information at all can be sent in the session. You'd be surprised at how many sites that do session management put client information into the session or cookies themselves, like IP or hostname. Don't.
    2. Always hash the information you pass to the client, and make sure to use good entropy when you do. There are a few sites onthe web that can check entropy of cookies for you for strength. Use them to test your cookies, sessions, and code before you go live with your site.
    3. Never assume the capabilities of the client, including their browser, proxy, cache, or connection. Each of these is subject to fail or perform in unpredictable ways at any time. Do not guess.
    4. Consider what you're passing in the session, and why, then consider it again. Is it really needed? Can you pass something else in a different way, and achieve the same result?

    Now the catch, Hidden Fields have their own set of flaws:

    1. Hidden form fields aren't really hidden. Any information stored in a hidden form field can be seen by anyone who views the source code of the web page. If you've stored runtime options to your CGI script, private user data, or anything else in hidden form fields that you don't want seen (or modified) by users, then you shouldn't use hidden form fields, or take some steps to obscure that information
    2. Hidden form fields allow users to gain unauthorized access to application functionality.
    3. Hidden form fields allow users to attack CGI programs using bad input
    4. Hidden form fields make it easier for users to break your state preservation mechanism. If you are using hidden fields to track state between pages (such as an online shopping cart), you are likely passing a session_id around between pages. If someone malscious were to get a hold of that session_id, they could jump in and hijack the session.

      Fortunately for us as designers, these types of attacks on a session management system are quite preventable, as long as the system is designed with security in mind. here's a few precautions you can take to protect yourself from these sorts of attacks:

      1. Never use user names or passwords, or other relevant data as the session_id. This makes it easier for malicious users to hijack your sessions. Also, generating a new session_id for each session allows you to address each session individually.
      2. Always make sure that your sessions time out after a specified period of inactivity. You want to give users enough time to process the content of each page before their session times out, but you don't want sessions to linger long after the user finishes with the site.
      3. Provide users with a "Sign Off" button, which allows them to terminate their session before they leave the site. This also provides a measure of protection that can prevent sessions from lingering after they're out of use.
      4. Map the session_id to another identifier, like the user's IP address (remember not to store it "in the clear" as mentioned before). This prevents users on one computer from hijacking the session from another, although it doesn't make it impossible.

      One way to generate a good solid session_id could be like this:

      use strict; use warnings; use Digest::MD5; my $md5 = new Digest::MD5; my $remote = $ENV{REMOTE_ADDR} . $ENV{REMOTE_PORT}; my $id = $md5->md5_base64( time, # current time $$, # process id $remote); # host and port $id =~ tr|+/=|-_.|; # escape the result return $id;

    Further reading:

    1. The W3C has a good explanation of similar issues on session security found here. Read it if you are considering building a site that incorporates sessions or tracking user interaction within your site or realm.
    2. Security and Privacy Topics from the University of Illinois WWW Identification Service.
    3. Web Security Course
    4. Web Application Forensics. Do not pass "Go", do not skip this one. Read it thoroughly.
    5. The WWW Security FAQ, covers HTTP, Java, Javascript, SSL, and more.
    6. Web Server Security Check, which can be used to check your browser's compliance, security, and ability to properly handle sessions in a secure fashion.

    There is no "wrong" way to do session management, but there are "strong" ways to do it.

    Don't just take the easy path of using cookies or storing the session_id in the URI, without considering the outcomes, and the other possibilities.

      Putting the session_id into the URI may fail in a lot of situations, such as with users behind a caching proxy.

      By "fail" do you mean that the pages will not be cacheable by the proxy? In that case, hidden form fields have exactly the same problem, since the content of the page has to be different for every user. Proxy servers don't cache POST requests and GET requests coming from a form submission look identical to URIs with query args on the end. Hidden fields also have the problem of making every single link a form submission, which is a real pain and means replacing text links with images and buttons.

      Also, your use of the remote IP as part of your session ID does not prevent people from hijacking a session, it just makes it harder to guess a valid one, and it is possible to create duplicate IDs when using this in a cluster. It would be safer to use mod_unique_id for generating the ID and then use a MAC to verify it when it gets sent back, as described here.

      Great explanation, hacker!

      I would like to add few considerations to what you said.

      • hidden fields make impossible to cache generated pages;
      • hidden fields can hold different session_ids for different instances of the same browser, and this can be useful sometimes;
      • this is impossible using cookies;
      • your way to generate session ids can be improved using the code coming right from the Eagle Book:
        use MD5 (); $MAC = MD5->hexhash($secret . MD5->hexhash(join '', $secret, @fields));

        Now you also have a secret string, not predictable by the client, and double md5, to prevent dirty tricks over digested string. There is a nice introduction about this at page 213 of the Eagle Book.

      Ciao, Valerio

Re: Secure Session Management
by glickjd (Novice) on Sep 06, 2002 at 23:19 UTC
    Thanks for the feedback guys. I was since my posting, I've done a little thinking and have an idea how to do this. Here's my idea, please let me know what you think.

    Each user will have a record in a database. When the user initially logs in, it will verify that they entered the correct username and password by comparing to the values in the database.

    After it verifies the username and password are correct, it will assign a random string of text (session ID) and it will write this session ID along with the current time, to the record in the database. It will also write this session ID along with the username to a cookie.

    Now when the user loads another page, it will pull the session ID and username from the cookie. After it finds the matching session ID and username in the database, it will check the time in the database (time session ID was assigned). If that time is over a certain limit, it will timeout and display the login screen...otherwise it will allow the user to continue and will assign a new session ID and time to the databse and the new session ID and username to the cookie.

    I figure since all these pages are encrypted with SSL, it's not a having the session ID intercepted is not a concern. Plus, the session ID is changed each time, so even if someone got a hold of it, it will have probably changed or timed out by the time they can use it.

    Any comments or suggestions? Do you see any problems here? Do you think this would hold up and perform well with a large amount of users?

    Thanks again,

    Jeremy

      Almost, but not quite (IMO). You dont need to send the username to the browser, and think about adding an expiry date to your session database.

      So:

      1. The user logs in
      2. You create a sess_id and send it to the browser (via a cookie or "param")
      3. You record the sess_id in the database along side a user_id
      4. Foreach pageview you retrieve the cookie from the browser and compare it to the one in your database
      5. You compare the "sysdate" with the date in you set in the session database(expy_date); if sysdate is > expy_date, send the login page to the browser.
      6. optionally on each pageview you can:
        1. Extend the session by n so the session is more dynamic and stay current as long as the user is actively using your application
        2. re-issue another sess_id - making your application a touch more secure.