Provided by: libcgi-session-perl_4.48-1_all bug

NAME

       CGI::Session::Tutorial - Extended CGI::Session manual

STATE MAINTENANCE OVERVIEW

       Since HTTP is a stateless protocol, each subsequent click to a web site is treated as new
       request by the Web server. The server does not relate a visit with a previous one, thus
       all the state information from the previous requests are lost. This makes creating such
       applications as shopping carts, web sites requiring users to authenticate, impossible. So
       people had to do something about this despair situation HTTP was putting us in.

       For our rescue come such technologies as HTTP Cookies and QUERY_STRINGs that help us save
       the users' session for a certain period. Since HTTP Cookies and QUERY_STRINGs alone cannot
       take us too far (RFC 2965, Section 5, "Implementation Limitations"), several other
       libraries have been developed to extend their capabilities and promise a more reliable
       solution. CGI::Session is one of them.

       Before we discuss this library, let's look at some alternative solutions.

   COOKIE
       Cookie is a piece of text-information that a web server is entitled to place in the user's
       hard disk, assuming a user agent (such as Internet Explorer, Mozilla, etc) is compatible
       with the specification. After the cookie is placed, user agents are required to send these
       cookies back to the server as part of the HTTP request. This way the server application (
       CGI, for example ) will have a way of relating previous requests by the same user agent,
       thus overcoming statelessness of HTTP.

       Although HTTP Cookies seem to be promising solution for the statelessness of HTTP, they do
       carry certain limitations, such as limited number of cookies per domain and per user agent
       and limited size on each cookie. User Agents are required to store at least 300 cookies at
       a time, 20 cookies per domain and allow 4096 bytes of storage for each cookie. They also
       rise several Privacy and Security concerns, the lists of which can be found on the
       sections 6-"Privacy"  and 7-"Security Considerations" of RFC 2965.

   QUERY STRING
       Query string is a string appended to URL following a question mark (?) such as:

           http://my.dot.com/login.cgi?user=sherzodr;password=top-secret

       As you probably guessed, it can also help you pass state information from a click to
       another, but how secure is it do you think, considering these URLs tend to get cached by
       most of the user agents and also logged in the servers access log, to which everyone can
       have access.

   HIDDEN FIELDS
       Hidden field is another alternative to using query strings and they come in two flavors:
       hidden fields used in POST methods and the ones in GET. The ones used in GET methods will
       turn into a true query strings once submitted, so all the disadvantages of QUERY_STRINGs
       apply. Although POST requests do not have limitations of its sister-GET, the pages that
       hold them get cached by Web browser, and are available within the source code of the page
       (obviously). They also become unwieldily to manage when one has oodles of state
       information to keep track of ( for instance, a shopping cart or an advanced search
       engine).

       Query strings and hidden fields are also lost easily by closing the browser, or by
       clicking the browser's "Back" button.

   SERVER SIDE SESSION MANAGEMENT
       This technique is built upon the aforementioned technologies plus a server-side storage
       device, which saves the state data on the server side. Each session has a unique id
       associated with the data in the server. This id is also associated with the user agent
       either in the form of a HTTP Cookie, a QUERY_STRING, hidden field or any combination of
       the above. This is necessary to make the connection with the client and his data.

       Advantages:

       •   We no longer need to depend on User Agent constraints in cookie size.

       •   Sensitive data no longer need to be traveling across the network at each request
           (which is the case with query strings, cookies and hidden fields). The only thing that
           travels is the unique id generated for the session (5767393932698093d0b75ef614376314,
           for instance), which should make no sense to third parties.

       •   User will not have sensitive data stored in his/her computer in unsecured file (which
           is a cookie file).

       •   It's possible to handle very big and even complex data structures transparently (which
           HTTP Cookies do not handle).

       That's what CGI::Session is all about - implementing server side session management. Now
       is a good time to get feet wet.

PROGRAMMING STYLE

       Server side session management system might be seeming awfully convoluted if you have
       never dealt with it. Fortunately, with CGI::Session all the complexity is handled by the
       library transparently. This section of the manual can be treated as an introductory
       tutorial to  both logic behind session management, and to CGI::Session programming style.

       All applications making use of server side session management rely on the following
       pattern of operation regardless of the way the system is implemented:

       1.  Check if the user has session cookie dropped in his computer from previous request

       2.  If the cookie does not exist, create a new session identifier, and drop it as cookie
           to the user's computer.

       3.  If session cookie exists, read the session ID from the cookie and load any previously
           saved session data from the server side storage. If session had any expiration date
           set it's useful to re-drop the same cookie to the user's computer so its expiration
           time will be reset to be relative to user's last activity time.

       4.  Store any necessary data in the session that you want to make available for the next
           HTTP request.

       CGI::Session will handle all of the above steps. All you have to do is to choose what to
       store in the session.

   GETTING STARTED
       To make CGI::Session's functionality available in your program do either of the following
       somewhere on top of your program file:

           use CGI::Session;
           # or
           require CGI::Session;

       Whenever you're ready to create a new session in your application, do the following:

           $session = CGI::Session->new () or die CGI::Session->errstr;

       Above line will first try to re-initialize an existing session by consulting cookies and
       necessary QUERY_STRING parameters. If it fails will create a brand new session with a
       unique ID, which is normally called session ID, SID for short, and can be accessed through
       id() - object method.

       We didn't check for any session cookies above, did we? No, we didn't, but CGI::Session
       did. It looked for a cookie called "CGISESSID", and if it found it tried to load existing
       session from server side storage (file in our case). If cookie didn't exist it looked for
       a QUERY_STRING parameter called "CGISESSID". If all the attempts to recover session ID
       failed, it created a new session.

       NOTE: For the above syntax to work as intended your application needs to have write access
       to your computer's TEMPDIR folder, which is usually /tmp in UNIX. If it doesn't, or if you
       wish to store this application's session files in a different place, you may pass the
       third argument like so:

           $session = CGI::Session->new(undef, undef, {Directory=>'../tmp/sessions'});

       Now it will store all the newly created sessions in (and will attempt to initialize
       requested sessions from) that folder. Don't worry if the directory hierarchy you want to
       use doesn't already exist. It will be created for you. For details on how session data are
       stored refer to CGI::Session::Driver::file, which is the default driver used in our above
       example.

       There is one small, but very important thing your application needs to perform after
       creating CGI::Session object as above. It needs to drop Session ID as an HTTP cookie into
       the user's computer. CGI::Session will use this cookie to identify the user at his/her
       next request and will be able to load his/her previously stored session data.

       To make sure CGI::Session will be able to read your cookie at next request you need to
       consult its "name()" method for cookie's suggested name:

           $cookie = $query->cookie( -name   => $session->name,
                                     -value  => $session->id );
           print $query->header( -cookie=>$cookie );

       "name()" returns "CGISESSID" by default. If you prefer a different cookie name, you can
       change it as easily too, but you have to do it before CGI::Session object is created:

           CGI::Session->name("SID");
           $session = CGI::Session->new();

       Baking the cookie wasn't too difficult, was it? But there is an even easier way to send a
       cookie using CGI::Session:

           print $session->header();

       The above will create the cookie using CGI::Cookie and will return proper http headers
       using CGI.pm's CGI method. Any arguments to CGI::Session will be passed to CGI::header().

       Of course, this method of initialization will only work if client is accepting cookies. If
       not you would have to pass session ID in each URL of your application as QUERY_STRING. For
       CGI::Session to detect it the name of the parameter should be the same as returned by
       name():

           printf ("<a href=\"$ENV{SCRIPT_NAME}?%s=%s\">click me</a>", $session->name, $session->id);

       If you already have session id to be initialized you may pass it as the only argument, or
       the second argument of multi-argument syntax:

           $session = CGI::Session->new( $sid );
           $session = CGI::Session->new( "serializer:freezethaw", $sid );
           $session = CGI::Session->new( "driver:mysql", $sid, {Handle=>$dbh} );

       By default CGI::Session uses standard CGI to parse queries and cookies. If you prefer to
       use a different, but compatible object you can pass that object in place of $sid:

           $cgi     = CGI::Simple->new();
           $session = CGI::Session->new( $cgi );
           $session = CGI::Session->new( "driver:db_file;serializer:storable", $cgi);
           # etc

       See CGI::Simple

   STORING DATA
       CGI::Session offers param() method, which behaves exactly as CGI.pm's param() with
       identical syntax. param() is used for storing data in session as well as for accessing
       already stored data.

       Imagine your customer submitted a login form on your Web site. You, as a good host, wanted
       to remember the guest's name, so you can a) greet him accordingly when he visits your site
       again, or b) to be helpful by filling out user name part of his login form, so the
       customer can jump right to the password field without having to type his username again.

           my $name = $cgi->param('username');
           $session->param('username', $name);

       Notice, we're grabbing username value of the field using CGI.pm's (or another compatible
       library's) "param()" method, and storing it in session using CGI::Session's param()
       method.

       If you have too many stuff to transfer into session, you may find yourself typing the
       above code over and over again. I've done it, and believe me, it gets very boring too
       soon, and is also error-prone. So we introduced the following handy method:

           $session->save_param(['name']);

       If you wanted to store multiple form fields just include them all in the second list:

           $session->save_param(['name', 'email']);

       If you want to store all the available QUERY_STRING parameters you can omit the arguments:

           $session->save_param();

       See save_param() for more details.

       When storing data in the session you're not limited to strings. You can store arrays,
       hashes and even most objects. You will need to pass them as references (except objects).

       For example, to get all the selected values of a scrolling list and store it in the
       session:

           my @fruits = $cgi->param('fruits');
           $session->param('fruits', \@fruits);

       For parameters with multiple values save_param() will do the right thing too. So the above
       is the same as:

           $session->save_param($cgi, ['fruits']);

       All the updates to the session data using above methods will not reflect in the data store
       until your application exits, or $session goes out of scope. If, for some reason, you need
       to commit the changes to the data store before your application exits you need to call
       flush() method:

           $session->flush();

       I've written a lot of code, and never felt need for using "flush()" method, since
       CGI::Session calls this method at the end of each request. There are, however, occasions I
       can think of one may need to call flush().

   ACCESSING STORED DATA
       There's no point of storing data if you cannot access it. You can access stored session
       data by using the same param() method you once used to store them. Remember the Username
       field from the previous section that we stored in the session? Let's read it back so we
       can partially fill the Login form for the user:

           $name = $session->param("name");
           printf "<input type=\"text\" name=\"name\" value=\"%s\" />", $name;

       To retrieve previously stored @fruits do not forget to de reference it:

           @fruits = @{ $session->param('fruits') };

       Very frequently, you may find yourself having to create pre-filled and pre-selected forms,
       like radio buttons, checkboxes and drop down menus according to the user's preferences or
       previous action. With text and textareas it's not a big deal - you can simply retrieve a
       single parameter from the session and hard code the value into the text field. But how
       would you do it when you have a group of radio buttons, checkboxes and scrolling lists?
       For this purpose, CGI::Session provides load_param() method, which loads given session
       parameters to a CGI object (assuming they have been previously saved with save_param() or
       alternative):

           $session->load_param($cgi, ["fruits"]);

       Now when you say:

           print $cgi->checkbox_group(fruits=>['apple', 'banana', 'apricot']);

       See load_param() for details.

       Generated checkboxes will be pre-filled using previously saved information.

       If you're making use of HTML::Template to separate the code from the skin, you can as well
       associate CGI::Session object with HTML::Template and access all the parameters from
       within HTML files. We love this trick!

           $template = HTML::Template->new(filename=>"some.tmpl", associate=>$session);
           print $template->output();

       Assuming the session object stored "first_name" and "email" parameters while being
       associated with HTML::Template, you can access those values from within your "some.tmpl"
       file now:

           Hello <a href="mailto:<TMPL_VAR email>"> <TMPL_VAR first_name> </a>!

       See HTML::Template's online manual for details.

   CLEARING SESSION DATA
       You store session data, you access session data and at some point you will want to clear
       certain session data, if not all. For this purpose CGI::Session provides clear() method
       which optionally takes one argument as an arrayref indicating which session parameters
       should be deleted from the session object:

           $session->clear(["~logged-in", "email"]);

       Above line deletes "~logged-in" and "email" session parameters from the session. And next
       time you say:

           $email = $session->param("email");

       it returns undef. If you omit the argument to clear(), be warned that all the session
       parameters you ever stored in the session object will get deleted. Note that it does not
       delete the session itself. Session stays open and accessible. It's just the parameters you
       stored in it gets deleted

       See clear() for details.

   DELETING A SESSION
       If there's a start there's an end. If session could be created, it should be possible to
       delete it from the disk for good:

           $session->delete();

       The above call to delete() deletes the session from the disk for good. Do not confuse it
       with clear(), which only clears certain session parameters but keeps the session open.

       See delete() for details.

   EXPIRATION
       CGI::Session provides limited means to expire sessions. Expiring a session is the same as
       deleting it via delete(), but deletion takes place automatically. To expire a session, you
       need to tell the library how long the session would be valid after the last access time.
       When that time is met, CGI::Session refuses to retrieve the session. It deletes the
       session and returns a brand new one. To assign expiration ticker for a session, use
       expire():

           $session->expire(3600);     # expire after 3600 seconds
           $session->expire('+1h');    # expire after 1 hour
           $session->expire('+15m');   # expire after 15 minutes
           $session->expire('+1M');    # expire after a month and so on.

       When session is set to expire at some time in the future, but session was not requested at
       or after that time has passed it will remain in the disk. When expired session is
       requested CGI::Session will remove the data from disk, and will initialize a brand new
       session.

       See expire() for details.

       Before CGI::Session 4.x there was no way of intercepting requests to expired sessions.
       CGI::Session 4.x introduced new kind of constructor, load(), which is identical in use to
       new(), but is not allowed to create sessions. It can only load them. If session is found
       to be expired, or session does not exist it will return an empty CGI::Session object. And
       if session is expired, in addition to being empty, its status will also be set to expired.
       You can check against these conditions using empty() and is_expired() methods. If session
       was loaded successfully object returned by "load()" is as good a session as the one
       returned by "new()":

           $session = CGI::Session->load() or die CGI::Session->errstr;
           if ( $session->is_expired ) {
               die "Your session expired. Please refresh your browser to re-start your session";
           }
           if ( $session->is_empty ) {
               $session = $session->new();
           }

       Above example is worth an attention. Remember, all expired sessions are empty sessions,
       but not all empty sessions are expired sessions. Following this rule we have to check with
       "is_expired()" before checking with "is_empty()". There is another thing about the above
       example. Notice how its creating new session when un existing session was requested? By
       calling "new()" as an object method! Handy thing about that is, when you call "new()" on a
       session object new object will be created using the same configuration as the previous
       object.

       For example:

           $session = CGI::Session->load("driver:mysql;serializer:storable", undef, {Handle=>$dbh});
           if ( $session->is_expired ) {
               die "Your session is expired. Please refresh your browser to re-start your session";
           }
           if ( $session->is_empty ) {
               $session = $session->new();
           }

       Initial $session object was configured with mysql as the driver, storable as the
       serializer and $dbh as the database handle. Calling " new() " on this object will return
       an object of the same configuration. So  $session  object returned from " new() " in the
       above example will use mysql as the driver, storable as the serializer and $dbh as the
       database handle.

       See is_expired(), is_empty(), load() for details.

       Sometimes it makes perfect sense to expire a certain session parameter, instead of the
       whole session. I usually do this in my login enabled sites, where after the user logs in
       successfully, I set his/her "_logged_in" session parameter to true, and assign an
       expiration ticker on that flag to something like 30 minutes. It means, after 30 idle
       minutes CGI::Session will clear "_logged_in" flag, indicating the user should log in over
       again. I agree, the same effect can be achieved by simply expiring() the session itself,
       but by doing this we would loose other session parameters, such as user's shopping cart,
       session-preferences and the like.

       This feature can also be used to simulate layered authentication, such as, you can keep
       the user's access to his/her personal profile information for as long as 60 minutes after
       a successful login, but expire his/her access to his credit card information after 5 idle
       minutes. To achieve this effect, we will use expire() method again:

           $session->expire(_profile_access, '1h');
           $session->expire(_cc_access, '5m');

       With the above syntax, the person will still have access to his personal information even
       after 5 idle hours. But when he tries to access or update his/her credit card information,
       he may be displayed a "login again, please" screen.

       See expire() for details.

       This concludes our discussion of CGI::Session programming style. The rest of the manual
       covers some "SECURITY" issues. Driver specs from the previous manual were moved to
       CGI::Session::Driver.

SECURITY

       "How secure is using CGI::Session?", "Can others hack down people's sessions using another
       browser if they can get the session id of the user?", "Are the session ids easy to guess?"
       are the questions I find myself answering over and over again.

   STORAGE
       Security of the library does in many aspects depend on the implementation. After making
       use of this library, you no longer have to send all the information to the user's cookie
       except for the session id. But, you still have to store the data in the server side. So
       another set of questions arise, can an evil person get access to session data in your
       server, even if he does, can he make sense out of the data in the session file, and even
       if he can, can he reuse the information against a person who created that session. As you
       see, the answer depends on yourself who is implementing it.

       •   First rule of thumb, do not store users' passwords or other sensitive data in the
           session, please. If you have to, use one-way encryption, such as md5, or SHA-1-1. For
           my own experience I can assure you that in properly implemented session-powered Web
           applications there is never a need for it.

       •   Default configuration of the driver makes use of Data::Dumper class to serialize data
           to make it possible to save it in the disk. Data::Dumper's result is a human readable
           data structure, which, if opened, can be interpreted easily. If you configure your
           session object to use either Storable or FreezeThaw as a serializer, this would make
           it more difficult for bad guys to make sense out of session data. But don't use this
           as the only precaution. Since evil fingers can type a quick program using Storable or
           FreezeThaw to decipher session files very easily.

       •   Do not allow anyone to update contents of session files. If you're using default
           serializer serialized data string needs to be eval()ed to bring the original data
           structure back to life. Of course, we use Safe to do it safely, but your cautiousness
           does no harm either.

       •   Do not keep sessions open for very long. This will increase the possibility that some
           bad guy may have someone's valid session id at a given time (acquired somehow). To do
           this use expire() method to set expiration ticker. The more sensitive the information
           on your Web site is, the sooner the session should be set to expire.

   SESSION IDs
       Session ids are not easily guessed (unless you're using incr ID generator)! Default
       configuration of CGI::Session uses Digest::MD5 to generate random, 32 character long
       identifier. Although this string cannot be guessed as easily by others, if they find it
       out somehow, can they use this identifier against the other person?

       Consider the scenario, where you just give someone either via email or an instant
       messaging a link to a Web site where you're currently logged in. The URL you give to that
       person contains a session id as part of a query string. If the site was initializing the
       session solely using query string parameter, after clicking on that link that person now
       appears to that site as you, and might have access to all of your private data instantly.

       Even if you're solely using cookies as the session id transporters, it's not that
       difficult to plant a cookie in the cookie file with the same id and trick the web browser
       to send that particular session id to the server. So key for security is to check if the
       person who's asking us to retrieve a session data is indeed the person who initially
       created the session data.

       One way to help with this is by also checking that the IP address that the session is
       being used from is always same. However, this turns out not to be practical in common
       cases because some large ISPs (such as AOL) use proxies which cause each and every request
       from the same user to come from different IP address.

       If you have an application where you are sure your users' IPs are constant during a
       session, you can consider enabling an option to make this check:

           use CGI::Session '-ip_match';

       For backwards compatibility, you can also achieve this by setting $CGI::Session::IP_MATCH
       to a true value.  This makes sure that before initializing a previously stored session, it
       checks if the ip address stored in the session matches the ip address of the user asking
       for that session. In which case the library returns the session, otherwise it dies with a
       proper error message.

LICENSING

       For support and licensing see CGI::Session