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


in reply to Secure Session Management

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.

Replies are listed 'Best First'.
Re: Re: Secure Session Management
by perrin (Chancellor) on Sep 15, 2002 at 15:40 UTC
    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.

Re: Re: Secure Session Management
by valdez (Monsignor) on Sep 08, 2002 at 21:37 UTC

    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