1CGI::Session::Tutorial(U3s)er Contributed Perl DocumentatCiGoIn::Session::Tutorial(3)
2
3
4
6 CGI::Session::Tutorial - Extended CGI::Session manual
7
9 Since HTTP is a stateless protocol, each subsequent click to a web site
10 is treated as new request by the Web server. The server does not relate
11 a visit with a previous one, thus all the state information from the
12 previous requests are lost. This makes creating such applications as
13 shopping carts, web sites requiring users to authenticate, impossible.
14 So people had to do something about this despair situation HTTP was
15 putting us in.
16
17 For our rescue come such technologies as HTTP Cookies and QUERY_STRINGs
18 that help us save the users' session for a certain period. Since HTTP
19 Cookies and QUERY_STRINGs alone cannot take us too far (RFC 2965,
20 Section 5, "Implementation Limitations"), several other libraries have
21 been developed to extend their capabilities and promise a more reliable
22 solution. CGI::Session is one of them.
23
24 Before we discuss this library, let's look at some alternative
25 solutions.
26
27 COOKIE
28 Cookie is a piece of text-information that a web server is entitled to
29 place in the user's hard disk, assuming a user agent (such as Internet
30 Explorer, Mozilla, etc) is compatible with the specification. After the
31 cookie is placed, user agents are required to send these cookies back
32 to the server as part of the HTTP request. This way the server
33 application ( CGI, for example ) will have a way of relating previous
34 requests by the same user agent, thus overcoming statelessness of HTTP.
35
36 Although HTTP Cookies seem to be promising solution for the
37 statelessness of HTTP, they do carry certain limitations, such as
38 limited number of cookies per domain and per user agent and limited
39 size on each cookie. User Agents are required to store at least 300
40 cookies at a time, 20 cookies per domain and allow 4096 bytes of
41 storage for each cookie. They also rise several Privacy and Security
42 concerns, the lists of which can be found on the sections 6-"Privacy"
43 and 7-"Security Considerations" of RFC 2965.
44
45 QUERY STRING
46 Query string is a string appended to URL following a question mark (?)
47 such as:
48
49 http://my.dot.com/login.cgi?user=sherzodr;password=top-secret
50
51 As you probably guessed, it can also help you pass state information
52 from a click to another, but how secure is it do you think, considering
53 these URLs tend to get cached by most of the user agents and also
54 logged in the servers access log, to which everyone can have access.
55
56 HIDDEN FIELDS
57 Hidden field is another alternative to using query strings and they
58 come in two flavors: hidden fields used in POST methods and the ones in
59 GET. The ones used in GET methods will turn into a true query strings
60 once submitted, so all the disadvantages of QUERY_STRINGs apply.
61 Although POST requests do not have limitations of its sister-GET, the
62 pages that hold them get cached by Web browser, and are available
63 within the source code of the page (obviously). They also become
64 unwieldily to manage when one has oodles of state information to keep
65 track of ( for instance, a shopping cart or an advanced search engine).
66
67 Query strings and hidden fields are also lost easily by closing the
68 browser, or by clicking the browser's "Back" button.
69
70 SERVER SIDE SESSION MANAGEMENT
71 This technique is built upon the aforementioned technologies plus a
72 server-side storage device, which saves the state data on the server
73 side. Each session has a unique id associated with the data in the
74 server. This id is also associated with the user agent either in the
75 form of a HTTP Cookie, a QUERY_STRING, hidden field or any combination
76 of the above. This is necessary to make the connection with the client
77 and his data.
78
79 Advantages:
80
81 • We no longer need to depend on User Agent constraints in cookie
82 size.
83
84 • Sensitive data no longer need to be traveling across the network at
85 each request (which is the case with query strings, cookies and
86 hidden fields). The only thing that travels is the unique id
87 generated for the session (5767393932698093d0b75ef614376314, for
88 instance), which should make no sense to third parties.
89
90 • User will not have sensitive data stored in his/her computer in
91 unsecured file (which is a cookie file).
92
93 • It's possible to handle very big and even complex data structures
94 transparently (which HTTP Cookies do not handle).
95
96 That's what CGI::Session is all about - implementing server side
97 session management. Now is a good time to get feet wet.
98
100 Server side session management system might be seeming awfully
101 convoluted if you have never dealt with it. Fortunately, with
102 CGI::Session all the complexity is handled by the library
103 transparently. This section of the manual can be treated as an
104 introductory tutorial to both logic behind session management, and to
105 CGI::Session programming style.
106
107 All applications making use of server side session management rely on
108 the following pattern of operation regardless of the way the system is
109 implemented:
110
111 1. Check if the user has session cookie dropped in his computer from
112 previous request
113
114 2. If the cookie does not exist, create a new session identifier, and
115 drop it as cookie to the user's computer.
116
117 3. If session cookie exists, read the session ID from the cookie and
118 load any previously saved session data from the server side
119 storage. If session had any expiration date set it's useful to re-
120 drop the same cookie to the user's computer so its expiration time
121 will be reset to be relative to user's last activity time.
122
123 4. Store any necessary data in the session that you want to make
124 available for the next HTTP request.
125
126 CGI::Session will handle all of the above steps. All you have to do is
127 to choose what to store in the session.
128
129 GETTING STARTED
130 To make CGI::Session's functionality available in your program do
131 either of the following somewhere on top of your program file:
132
133 use CGI::Session;
134 # or
135 require CGI::Session;
136
137 Whenever you're ready to create a new session in your application, do
138 the following:
139
140 $session = CGI::Session->new () or die CGI::Session->errstr;
141
142 Above line will first try to re-initialize an existing session by
143 consulting cookies and necessary QUERY_STRING parameters. If it fails
144 will create a brand new session with a unique ID, which is normally
145 called session ID, SID for short, and can be accessed through id() -
146 object method.
147
148 We didn't check for any session cookies above, did we? No, we didn't,
149 but CGI::Session did. It looked for a cookie called "CGISESSID", and if
150 it found it tried to load existing session from server side storage
151 (file in our case). If cookie didn't exist it looked for a QUERY_STRING
152 parameter called "CGISESSID". If all the attempts to recover session ID
153 failed, it created a new session.
154
155 NOTE: For the above syntax to work as intended your application needs
156 to have write access to your computer's TEMPDIR folder, which is
157 usually /tmp in UNIX. If it doesn't, or if you wish to store this
158 application's session files in a different place, you may pass the
159 third argument like so:
160
161 $session = CGI::Session->new(undef, undef, {Directory=>'../tmp/sessions'});
162
163 Now it will store all the newly created sessions in (and will attempt
164 to initialize requested sessions from) that folder. Don't worry if the
165 directory hierarchy you want to use doesn't already exist. It will be
166 created for you. For details on how session data are stored refer to
167 CGI::Session::Driver::file, which is the default driver used in our
168 above example.
169
170 There is one small, but very important thing your application needs to
171 perform after creating CGI::Session object as above. It needs to drop
172 Session ID as an HTTP cookie into the user's computer. CGI::Session
173 will use this cookie to identify the user at his/her next request and
174 will be able to load his/her previously stored session data.
175
176 To make sure CGI::Session will be able to read your cookie at next
177 request you need to consult its name() method for cookie's suggested
178 name:
179
180 $cookie = $query->cookie( -name => $session->name,
181 -value => $session->id );
182 print $query->header( -cookie=>$cookie );
183
184 name() returns "CGISESSID" by default. If you prefer a different cookie
185 name, you can change it as easily too, but you have to do it before
186 CGI::Session object is created:
187
188 CGI::Session->name("SID");
189 $session = CGI::Session->new();
190
191 Baking the cookie wasn't too difficult, was it? But there is an even
192 easier way to send a cookie using CGI::Session:
193
194 print $session->header();
195
196 The above will create the cookie using CGI::Cookie and will return
197 proper http headers using CGI.pm's CGI method. Any arguments to
198 CGI::Session will be passed to CGI::header().
199
200 Of course, this method of initialization will only work if client is
201 accepting cookies. If not you would have to pass session ID in each URL
202 of your application as QUERY_STRING. For CGI::Session to detect it the
203 name of the parameter should be the same as returned by name():
204
205 printf ("<a href=\"$ENV{SCRIPT_NAME}?%s=%s\">click me</a>", $session->name, $session->id);
206
207 If you already have session id to be initialized you may pass it as the
208 only argument, or the second argument of multi-argument syntax:
209
210 $session = CGI::Session->new( $sid );
211 $session = CGI::Session->new( "serializer:freezethaw", $sid );
212 $session = CGI::Session->new( "driver:mysql", $sid, {Handle=>$dbh} );
213
214 By default CGI::Session uses standard CGI to parse queries and cookies.
215 If you prefer to use a different, but compatible object you can pass
216 that object in place of $sid:
217
218 $cgi = CGI::Simple->new();
219 $session = CGI::Session->new( $cgi );
220 $session = CGI::Session->new( "driver:db_file;serializer:storable", $cgi);
221 # etc
222
223 See CGI::Simple
224
225 STORING DATA
226 CGI::Session offers param() method, which behaves exactly as CGI.pm's
227 param() with identical syntax. param() is used for storing data in
228 session as well as for accessing already stored data.
229
230 Imagine your customer submitted a login form on your Web site. You, as
231 a good host, wanted to remember the guest's name, so you can a) greet
232 him accordingly when he visits your site again, or b) to be helpful by
233 filling out user name part of his login form, so the customer can jump
234 right to the password field without having to type his username again.
235
236 my $name = $cgi->param('username');
237 $session->param('username', $name);
238
239 Notice, we're grabbing username value of the field using CGI.pm's (or
240 another compatible library's) param() method, and storing it in session
241 using CGI::Session's param() method.
242
243 If you have too many stuff to transfer into session, you may find
244 yourself typing the above code over and over again. I've done it, and
245 believe me, it gets very boring too soon, and is also error-prone. So
246 we introduced the following handy method:
247
248 $session->save_param(['name']);
249
250 If you wanted to store multiple form fields just include them all in
251 the second list:
252
253 $session->save_param(['name', 'email']);
254
255 If you want to store all the available QUERY_STRING parameters you can
256 omit the arguments:
257
258 $session->save_param();
259
260 See save_param() for more details.
261
262 When storing data in the session you're not limited to strings. You can
263 store arrays, hashes and even most objects. You will need to pass them
264 as references (except objects).
265
266 For example, to get all the selected values of a scrolling list and
267 store it in the session:
268
269 my @fruits = $cgi->param('fruits');
270 $session->param('fruits', \@fruits);
271
272 For parameters with multiple values save_param() will do the right
273 thing too. So the above is the same as:
274
275 $session->save_param($cgi, ['fruits']);
276
277 All the updates to the session data using above methods will not
278 reflect in the data store until your application exits, or $session
279 goes out of scope. If, for some reason, you need to commit the changes
280 to the data store before your application exits you need to call
281 flush() method:
282
283 $session->flush();
284
285 I've written a lot of code, and never felt need for using flush()
286 method, since CGI::Session calls this method at the end of each
287 request. There are, however, occasions I can think of one may need to
288 call flush().
289
290 ACCESSING STORED DATA
291 There's no point of storing data if you cannot access it. You can
292 access stored session data by using the same param() method you once
293 used to store them. Remember the Username field from the previous
294 section that we stored in the session? Let's read it back so we can
295 partially fill the Login form for the user:
296
297 $name = $session->param("name");
298 printf "<input type=\"text\" name=\"name\" value=\"%s\" />", $name;
299
300 To retrieve previously stored @fruits do not forget to de reference it:
301
302 @fruits = @{ $session->param('fruits') };
303
304 Very frequently, you may find yourself having to create pre-filled and
305 pre-selected forms, like radio buttons, checkboxes and drop down menus
306 according to the user's preferences or previous action. With text and
307 textareas it's not a big deal - you can simply retrieve a single
308 parameter from the session and hard code the value into the text field.
309 But how would you do it when you have a group of radio buttons,
310 checkboxes and scrolling lists? For this purpose, CGI::Session provides
311 load_param() method, which loads given session parameters to a CGI
312 object (assuming they have been previously saved with save_param() or
313 alternative):
314
315 $session->load_param($cgi, ["fruits"]);
316
317 Now when you say:
318
319 print $cgi->checkbox_group(fruits=>['apple', 'banana', 'apricot']);
320
321 See load_param() for details.
322
323 Generated checkboxes will be pre-filled using previously saved
324 information.
325
326 If you're making use of HTML::Template to separate the code from the
327 skin, you can as well associate CGI::Session object with HTML::Template
328 and access all the parameters from within HTML files. We love this
329 trick!
330
331 $template = HTML::Template->new(filename=>"some.tmpl", associate=>$session);
332 print $template->output();
333
334 Assuming the session object stored "first_name" and "email" parameters
335 while being associated with HTML::Template, you can access those values
336 from within your "some.tmpl" file now:
337
338 Hello <a href="mailto:<TMPL_VAR email>"> <TMPL_VAR first_name> </a>!
339
340 See HTML::Template's online manual for details.
341
342 CLEARING SESSION DATA
343 You store session data, you access session data and at some point you
344 will want to clear certain session data, if not all. For this purpose
345 CGI::Session provides clear() method which optionally takes one
346 argument as an arrayref indicating which session parameters should be
347 deleted from the session object:
348
349 $session->clear(["~logged-in", "email"]);
350
351 Above line deletes "~logged-in" and "email" session parameters from the
352 session. And next time you say:
353
354 $email = $session->param("email");
355
356 it returns undef. If you omit the argument to clear(), be warned that
357 all the session parameters you ever stored in the session object will
358 get deleted. Note that it does not delete the session itself. Session
359 stays open and accessible. It's just the parameters you stored in it
360 gets deleted
361
362 See clear() for details.
363
364 DELETING A SESSION
365 If there's a start there's an end. If session could be created, it
366 should be possible to delete it from the disk for good:
367
368 $session->delete();
369
370 The above call to delete() deletes the session from the disk for good.
371 Do not confuse it with clear(), which only clears certain session
372 parameters but keeps the session open.
373
374 See delete() for details.
375
376 EXPIRATION
377 CGI::Session provides limited means to expire sessions. Expiring a
378 session is the same as deleting it via delete(), but deletion takes
379 place automatically. To expire a session, you need to tell the library
380 how long the session would be valid after the last access time. When
381 that time is met, CGI::Session refuses to retrieve the session. It
382 deletes the session and returns a brand new one. To assign expiration
383 ticker for a session, use expire():
384
385 $session->expire(3600); # expire after 3600 seconds
386 $session->expire('+1h'); # expire after 1 hour
387 $session->expire('+15m'); # expire after 15 minutes
388 $session->expire('+1M'); # expire after a month and so on.
389
390 When session is set to expire at some time in the future, but session
391 was not requested at or after that time has passed it will remain in
392 the disk. When expired session is requested CGI::Session will remove
393 the data from disk, and will initialize a brand new session.
394
395 See expire() for details.
396
397 Before CGI::Session 4.x there was no way of intercepting requests to
398 expired sessions. CGI::Session 4.x introduced new kind of constructor,
399 load(), which is identical in use to new(), but is not allowed to
400 create sessions. It can only load them. If session is found to be
401 expired, or session does not exist it will return an empty CGI::Session
402 object. And if session is expired, in addition to being empty, its
403 status will also be set to expired. You can check against these
404 conditions using empty() and is_expired() methods. If session was
405 loaded successfully object returned by load() is as good a session as
406 the one returned by new():
407
408 $session = CGI::Session->load() or die CGI::Session->errstr;
409 if ( $session->is_expired ) {
410 die "Your session expired. Please refresh your browser to re-start your session";
411 }
412 if ( $session->is_empty ) {
413 $session = $session->new();
414 }
415
416 Above example is worth an attention. Remember, all expired sessions are
417 empty sessions, but not all empty sessions are expired sessions.
418 Following this rule we have to check with is_expired() before checking
419 with is_empty(). There is another thing about the above example. Notice
420 how its creating new session when un existing session was requested? By
421 calling new() as an object method! Handy thing about that is, when you
422 call new() on a session object new object will be created using the
423 same configuration as the previous object.
424
425 For example:
426
427 $session = CGI::Session->load("driver:mysql;serializer:storable", undef, {Handle=>$dbh});
428 if ( $session->is_expired ) {
429 die "Your session is expired. Please refresh your browser to re-start your session";
430 }
431 if ( $session->is_empty ) {
432 $session = $session->new();
433 }
434
435 Initial $session object was configured with mysql as the driver,
436 storable as the serializer and $dbh as the database handle. Calling
437 new() on this object will return an object of the same configuration.
438 So $session object returned from new() in the above example will
439 use mysql as the driver, storable as the serializer and $dbh as the
440 database handle.
441
442 See is_expired(), is_empty(), load() for details.
443
444 Sometimes it makes perfect sense to expire a certain session parameter,
445 instead of the whole session. I usually do this in my login enabled
446 sites, where after the user logs in successfully, I set his/her
447 "_logged_in" session parameter to true, and assign an expiration ticker
448 on that flag to something like 30 minutes. It means, after 30 idle
449 minutes CGI::Session will clear "_logged_in" flag, indicating the user
450 should log in over again. I agree, the same effect can be achieved by
451 simply expiring() the session itself, but by doing this we would loose
452 other session parameters, such as user's shopping cart, session-
453 preferences and the like.
454
455 This feature can also be used to simulate layered authentication, such
456 as, you can keep the user's access to his/her personal profile
457 information for as long as 60 minutes after a successful login, but
458 expire his/her access to his credit card information after 5 idle
459 minutes. To achieve this effect, we will use expire() method again:
460
461 $session->expire(_profile_access, '1h');
462 $session->expire(_cc_access, '5m');
463
464 With the above syntax, the person will still have access to his
465 personal information even after 5 idle hours. But when he tries to
466 access or update his/her credit card information, he may be displayed a
467 "login again, please" screen.
468
469 See expire() for details.
470
471 This concludes our discussion of CGI::Session programming style. The
472 rest of the manual covers some "SECURITY" issues. Driver specs from the
473 previous manual were moved to CGI::Session::Driver.
474
476 "How secure is using CGI::Session?", "Can others hack down people's
477 sessions using another browser if they can get the session id of the
478 user?", "Are the session ids easy to guess?" are the questions I find
479 myself answering over and over again.
480
481 STORAGE
482 Security of the library does in many aspects depend on the
483 implementation. After making use of this library, you no longer have to
484 send all the information to the user's cookie except for the session
485 id. But, you still have to store the data in the server side. So
486 another set of questions arise, can an evil person get access to
487 session data in your server, even if he does, can he make sense out of
488 the data in the session file, and even if he can, can he reuse the
489 information against a person who created that session. As you see, the
490 answer depends on yourself who is implementing it.
491
492 • First rule of thumb, do not store users' passwords or other
493 sensitive data in the session, please. If you have to, use one-way
494 encryption, such as md5, or SHA-1-1. For my own experience I can
495 assure you that in properly implemented session-powered Web
496 applications there is never a need for it.
497
498 • Default configuration of the driver makes use of Data::Dumper class
499 to serialize data to make it possible to save it in the disk.
500 Data::Dumper's result is a human readable data structure, which, if
501 opened, can be interpreted easily. If you configure your session
502 object to use either Storable or FreezeThaw as a serializer, this
503 would make it more difficult for bad guys to make sense out of
504 session data. But don't use this as the only precaution. Since evil
505 fingers can type a quick program using Storable or FreezeThaw to
506 decipher session files very easily.
507
508 • Do not allow anyone to update contents of session files. If you're
509 using default serializer serialized data string needs to be
510 eval()ed to bring the original data structure back to life. Of
511 course, we use Safe to do it safely, but your cautiousness does no
512 harm either.
513
514 • Do not keep sessions open for very long. This will increase the
515 possibility that some bad guy may have someone's valid session id
516 at a given time (acquired somehow). To do this use expire() method
517 to set expiration ticker. The more sensitive the information on
518 your Web site is, the sooner the session should be set to expire.
519
520 SESSION IDs
521 Session ids are not easily guessed (unless you're using incr ID
522 generator)! Default configuration of CGI::Session uses Digest::MD5 to
523 generate random, 32 character long identifier. Although this string
524 cannot be guessed as easily by others, if they find it out somehow, can
525 they use this identifier against the other person?
526
527 Consider the scenario, where you just give someone either via email or
528 an instant messaging a link to a Web site where you're currently logged
529 in. The URL you give to that person contains a session id as part of a
530 query string. If the site was initializing the session solely using
531 query string parameter, after clicking on that link that person now
532 appears to that site as you, and might have access to all of your
533 private data instantly.
534
535 Even if you're solely using cookies as the session id transporters,
536 it's not that difficult to plant a cookie in the cookie file with the
537 same id and trick the web browser to send that particular session id to
538 the server. So key for security is to check if the person who's asking
539 us to retrieve a session data is indeed the person who initially
540 created the session data.
541
542 One way to help with this is by also checking that the IP address that
543 the session is being used from is always same. However, this turns out
544 not to be practical in common cases because some large ISPs (such as
545 AOL) use proxies which cause each and every request from the same user
546 to come from different IP address.
547
548 If you have an application where you are sure your users' IPs are
549 constant during a session, you can consider enabling an option to make
550 this check:
551
552 use CGI::Session '-ip_match';
553
554 For backwards compatibility, you can also achieve this by setting
555 $CGI::Session::IP_MATCH to a true value. This makes sure that before
556 initializing a previously stored session, it checks if the ip address
557 stored in the session matches the ip address of the user asking for
558 that session. In which case the library returns the session, otherwise
559 it dies with a proper error message.
560
562 For support and licensing see CGI::Session
563
564
565
566perl v5.36.0 2023-01-20 CGI::Session::Tutorial(3)