Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
Catalyst::Plugin::SessUser:Contributed PCatalyst::Plugin::Session::Tutorial(3)

NAME
       Catalyst::Plugin::Session::Tutorial - Understanding and using sessions.

ASSUMPTIONS
       This tutorial assumes that you are familiar with	web applications in
       general and Catalyst specifically (up to	models and configuration), and
       that you	know what HTTP is.

WHAT ARE SESSIONS
       When users use a	site, especially one that knows	who they are (sites
       you log in to, sites which let you keep a shopping cart,	etc.), the
       server preparing	the content has	to know	that request X comes from
       client A	while request Y	comes from client B, so	that each user gets
       the content meant for them.

       The problem is that HTTP	is a stateless protocol. This means that every
       request is distinct, and	even if	it comes from the same client, it's
       difficult to know that.

       The way sessions	are maintained between distinct	requests is that the
       client says, for	every request, "I'm client A" or "I'm client B".

       This piece of data that tells the server	"I'm X"	is called the session
       ID, and the threading of	several	requests together is called a session.

HOW SESSIONS WORK
   Cookies
       HTTP has	a feature that lets this become	easier,	called cookies.	A
       cookie is something the server asks the client to save somewhere, and
       resend every time a request is made.

       The way they work is that the server sends the "Set-Cookie" header,
       with a cookie name, a value, and	some metadata (like when it expires,
       what paths it applies to, etc.).	The client saves this.

       Then, on	every subsequent request the client will send a	"Cookie"
       header, with the	cookie name and	value.

   Cookie Alternatives
       Another way is to make sure that	the session ID is repeated is to
       include it in every URI.

       This can	be as either a part of the path, or as a query parameter.

       This technique has several issues which are discussed in	"CAVEATS" in
       Catalyst::Plugin::Session::State::URI.

   Server-Side Behavior
       When the	server receives	the session ID it can then look	this key up in
       a database of some sort.	For example the	database can contain a
       shopping	cart's contents, user preferences, etc.

USING SESSIONS
       In Catalyst, the	Catalyst::Plugin::Session plugin provides an API for
       convenient handling of session data. This API is	based on the older,
       less flexible and less reliable Catalyst::Plugin::Session::FastMmap.

       The plugin is modular, and requires backend plugins to be used.

   State Plugins
       State plugins handle session ID persistence. For	example
       Catalyst::Plugin::Session::State::Cookie	creates	a cookie with the
       session ID in it.

       These plugins will automatically	set "$c->sessionid" at the beginning
       of the request, and automatically cause "$c->sessionid" to be saved by
       the client at the end of	the request.

   Store Plugins
       The backend into	which session data is stored is	provided by these
       plugins.	For example, Catalyst::Plugin::Session::Store::DBI uses	a
       database	table to store session data, while
       Catalyst::Plugin::Session::Store::FastMmap uses Cache::FastMmap.

   Configuration
       First you need to load the appropriate plugins into your	Catalyst
       application:

	   package MyApp;

	   use Catalyst	qw/
	       Session
	       Session::State::Cookie
	       Session::Store::File
	   /;

       This loads the session API, as well as the required backends of your
       choice.

       After the plugins are loaded they need to be configured.	This is	done
       according to "Configure_your_application" in
       Catalyst::Manual::Cookbook.

       Each backend plugin requires its	own configuration options (with	most
       plugins providing sensible defaults). The session API itself also has
       configurable options listed in "CONFIGURATION" in
       Catalyst::Plugin::Session.

       For the plugins above we	don't need any configuration at	all - they
       should work out of the box, but suppose we did want to change some
       things around, it'll look like this:

	   MyApp->config( 'Plugin::Session' => {
	       cookie_name => "my_fabulous_cookie",
	       storage	   => "/path/to/store_data_file",
	   });

   Usage
       Now, let's say we have an online	shop, and the user is adding an	item
       to the shopping cart.

       Typically the item the user was viewing would have a form or link that
       adds the	item to	the cart.

       Suppose this link goes to "/cart/add/foo_baz/2",	meaning	that we	want
       two units of the	item "foo_baz" to be added to the cart.

       Our "add" action	should look something like this:

	   package MyApp::Controller::Cart;

	   sub add : Local {
	       my ( $self, $c, $item_id, $quantity ) = @_;
	       $quantity ||= 1;

	       if ( $c->model("Items")->item_exists($item_id) )	{
		   $c->session->{cart}{$item_id} += $quantity;
	       } else {
		   die "No such	item";
	       }
	   }

       The way this works is that "$c->session"	always returns a hash
       reference to some data which is stored by the storage backend plugin.
       The hash	reference returned always contains the same items that were in
       there at	the end	of the last request.

       All the mishmash	described above	is done	automatically. First, the
       method looks to see if a	session	ID is set. This	session	ID will	be set
       by the State plugin if appropriate, at the start	of the request (e.g.
       by looking at the cookies sent by the client).

       If a session ID is set, the store will be asked to retrieve the session
       data for	that specific session ID, and this is returned from
       "$c->session". This retrieval is	cached,	and will only happen once per
       request,	if at all.

       If a session ID is not set, a new one is	generated, a new anonymous
       hash is created and saved in the	store with the session ID as the key,
       and the reference to the	hash is	returned.

       The action above	takes this hash	reference, and updates a nested	hash
       within it, that counts quantity of each item as stored in the cart.

       Any cart-listing	code can then look into	the session data and use it to
       display the correct items, which	will, of course, be remembered across
       requests.

       Here is an action some Template Toolkit example code that could be used
       to generate a cart listing:

	   sub list_cart : Local {
	       my ( $self, $c )	= @_;

	       # get the cart data, that maps from item_id to quantity
	       my $cart	= $c->session->{cart} || {};

	       # this is our abstract model in which items are stored
	       my $storage = $c->model("Items");

	       # map from item_id to item (an object or	hash reference)
	       my %items = map { $_ => $storage->get_item($_) }	keys %$cart;

	       # put the relevant info on the stash
	       $c->stash->{cart}{items}	= \%items;
	       $c->stash->{cart}{quantity} = $cart;
	   }

       And [a part of] the template it forwards	to:

	   <table>

	       <thead>
		   <tr>
		       <th>Item</th>
		       <th>Quantity</th>
		       <th>Price</th>
		       <th>remove</th>
		   </tr>
	       </thead>

	       <tbody>
	       [%# the table body lists	all the	items in the cart %]
	       [% FOREACH item_id = cart.items.keys %]

		   [%# each item has its own row in the	table %]

		   [% item = cart.items.$item_id %]
		   [% quantity = cart.quantity.$item_id	%]

		   <tr>
		       <td>
			   [%# item.name is an attribute in the	item
			     # object, as loaded from the store	%]
			   [% item.name	%]
		       </td>

		       <td>
			   [%# supposedly this is part of a form where you
			     # can update the quantity %]
			   <input type="text" name="[% item_id %]_quantity"
			       value="[% quantity %]" />
		       </td>

		       <td> $ [% item.price * quantity %] </td>

		       <td>
			   <a href="[% c.uri_for('/cart/remove') %]/[% item_id %]">
			       <img src="/static/trash_can.png"	/>
			   </a>
		       </td>
	       [% END %]
	       <tbody>

	       <tfoot>
		   <tr>
		       <td colspan="2">	Total: </td>
		       <td>
			   [%# calculate sum in	this cell - too
			     # much headache for a tutorial ;-)	%]
		       </td>
		       <td>
			   <a href="[% c.uri_for('/cart/empty')	%]">Empty cart</a>
		       </td>
		   </tr>
	       </tfoot>

	   </table>

       As you can see the way that items are added into	"$c->session->{cart}"
       is pretty simple. Since "$c->session" is	restored as necessary, and
       contains	data from previous requests by the same	client,	the cart can
       be updated as the user navigates	the site pretty	transparently.

SECURITY ISSUES
       These issues all	relate to how session data is managed, as described
       above.  These are not issues you	should be concerned about in your
       application code, but are here for their	educational value.

   (Not) Trusting the Client
       In order	to avoid the overhead of server-side data storage, the session
       data can	be included in the cookie itself.

       There are two problems with this:

       1.  The user can	change the data.

       2.  Cookies have	a 4 kilobyte size limit.

	   The size limit is of	no concern in this section, but	data changing
	   is. In the database scheme the data can be trusted, since the user
	   can neither read nor	write it. However, if the data is delegated to
	   the user, then special measures have	to be added for	ensuring data
	   integrity, and perhaps secrecy too.

	   This	can be implemented by encrypting and signing the cookie	data,
	   but this is a big headache.

   Session Hijacking
       What happens when client	B says "I'm client A"?	Well, basically, the
       server buys it. There's no real way around it.

       The solution is to make "I'm client A" a	difficult thing	to say.	This
       is why session IDs are randomized. If they are properly randomized,
       session IDs are so hard to guess	that they must be stolen instead.

       This is called session hijacking. There are several ways	one might
       hijack another user's session.

       Cross Site Scripting

       One is by using cross site scripting attacks to steal the cookie	data.
       In community sites, where users can cause the server to display
       arbitrary HTML, they can	use this to put	JavaScript code	on the server.

       If the server does not enforce a	strict subset of tags that may be
       used, the malicious user	could use this code to steal the cookies
       (there is a JavaScript API that lets cookies be accessed, but this code
       has to be run on	the same website that the cookie came from).

       Social Engineering

       By tricking a user into revealing a URI with session data embedded in
       it (when	cookies	are not	used), the session ID can also be stolen.

       Also, a naive user could	be tricked into	showing	the cookie data	from
       the browser to a	malicious user.

AUTHOR
       Yuval Kogman <nothingmuch@woobling.org>

perl v5.32.1			  2015-0Catalyst::Plugin::Session::Tutorial(3)

NAME | ASSUMPTIONS | WHAT ARE SESSIONS | HOW SESSIONS WORK | USING SESSIONS | SECURITY ISSUES | AUTHOR

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=Catalyst::Plugin::Session::Tutorial&sektion=3&manpath=FreeBSD+13.0-RELEASE+and+Ports>

home | help