1Catalyst::Plugin::SessiUosne:r:TCuotnotrriiablu(t3e)d PeCraltaDloycsutm:e:nPtlautgiionn::Session::Tutorial(3)
2
3
4

NAME

6       Catalyst::Plugin::Session::Tutorial - Understanding and using sessions.
7

ASSUMPTIONS

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

WHAT ARE SESSIONS

14       When users use a site, especially one that knows who they are (sites
15       you log in to, sites which let you keep a shopping cart, etc.), the
16       server preparing the content has to know that request X comes from
17       client A while request Y comes from client B, so that each user gets
18       the content meant for them.
19
20       The problem is that HTTP is a stateless protocol. This means that every
21       request is distinct, and even if it comes from the same client, it's
22       difficult to know that.
23
24       The way sessions are maintained between distinct requests is that the
25       client says, for every request, "I'm client A" or "I'm client B".
26
27       This piece of data that tells the server "I'm X" is called the session
28       ID, and the threading of several requests together is called a session.
29

HOW SESSIONS WORK

31   Cookies
32       HTTP has a feature that lets this become easier, called cookies. A
33       cookie is something the server asks the client to save somewhere, and
34       resend every time a request is made.
35
36       The way they work is that the server sends the "Set-Cookie" header,
37       with a cookie name, a value, and some metadata (like when it expires,
38       what paths it applies to, etc.). The client saves this.
39
40       Then, on every subsequent request the client will send a "Cookie"
41       header, with the cookie name and value.
42
43   Cookie Alternatives
44       Another way is to make sure that the session ID is repeated is to
45       include it in every URI.
46
47       This can be as either a part of the path, or as a query parameter.
48
49       This technique has several issues which are discussed in "CAVEATS" in
50       Catalyst::Plugin::Session::State::URI.
51
52   Server-Side Behavior
53       When the server receives the session ID it can then look this key up in
54       a database of some sort. For example the database can contain a
55       shopping cart's contents, user preferences, etc.
56

USING SESSIONS

58       In Catalyst, the Catalyst::Plugin::Session plugin provides an API for
59       convenient handling of session data. This API is based on the older,
60       less flexible and less reliable Catalyst::Plugin::Session::FastMmap.
61
62       The plugin is modular, and requires backend plugins to be used.
63
64   State Plugins
65       State plugins handle session ID persistence. For example
66       Catalyst::Plugin::Session::State::Cookie creates a cookie with the
67       session ID in it.
68
69       These plugins will automatically set "$c->sessionid" at the beginning
70       of the request, and automatically cause "$c->sessionid" to be saved by
71       the client at the end of the request.
72
73   Store Plugins
74       The backend into which session data is stored is provided by these
75       plugins. For example, Catalyst::Plugin::Session::Store::DBI uses a
76       database table to store session data, while
77       Catalyst::Plugin::Session::Store::FastMmap uses Cache::FastMmap.
78
79   Configuration
80       First you need to load the appropriate plugins into your Catalyst
81       application:
82
83           package MyApp;
84
85           use Catalyst qw/
86               Session
87               Session::State::Cookie
88               Session::Store::File
89           /;
90
91       This loads the session API, as well as the required backends of your
92       choice.
93
94       After the plugins are loaded they need to be configured. This is done
95       according to "Configure_your_application" in
96       Catalyst::Manual::Cookbook.
97
98       Each backend plugin requires its own configuration options (with most
99       plugins providing sensible defaults). The session API itself also has
100       configurable options listed in "CONFIGURATION" in
101       Catalyst::Plugin::Session.
102
103       For the plugins above we don't need any configuration at all - they
104       should work out of the box, but suppose we did want to change some
105       things around, it'll look like this:
106
107           MyApp->config( 'Plugin::Session' => {
108               cookie_name => "my_fabulous_cookie",
109               storage     => "/path/to/store_data_file",
110           });
111
112   Usage
113       Now, let's say we have an online shop, and the user is adding an item
114       to the shopping cart.
115
116       Typically the item the user was viewing would have a form or link that
117       adds the item to the cart.
118
119       Suppose this link goes to "/cart/add/foo_baz/2", meaning that we want
120       two units of the item "foo_baz" to be added to the cart.
121
122       Our "add" action should look something like this:
123
124           package MyApp::Controller::Cart;
125
126           sub add : Local {
127               my ( $self, $c, $item_id, $quantity ) = @_;
128               $quantity ||= 1;
129
130               if ( $c->model("Items")->item_exists($item_id) ) {
131                   $c->session->{cart}{$item_id} += $quantity;
132               } else {
133                   die "No such item";
134               }
135           }
136
137       The way this works is that "$c->session" always returns a hash
138       reference to some data which is stored by the storage backend plugin.
139       The hash reference returned always contains the same items that were in
140       there at the end of the last request.
141
142       All the mishmash described above is done automatically. First, the
143       method looks to see if a session ID is set. This session ID will be set
144       by the State plugin if appropriate, at the start of the request (e.g.
145       by looking at the cookies sent by the client).
146
147       If a session ID is set, the store will be asked to retrieve the session
148       data for that specific session ID, and this is returned from
149       "$c->session". This retrieval is cached, and will only happen once per
150       request, if at all.
151
152       If a session ID is not set, a new one is generated, a new anonymous
153       hash is created and saved in the store with the session ID as the key,
154       and the reference to the hash is returned.
155
156       The action above takes this hash reference, and updates a nested hash
157       within it, that counts quantity of each item as stored in the cart.
158
159       Any cart-listing code can then look into the session data and use it to
160       display the correct items, which will, of course, be remembered across
161       requests.
162
163       Here is an action some Template Toolkit example code that could be used
164       to generate a cart listing:
165
166           sub list_cart : Local {
167               my ( $self, $c ) = @_;
168
169               # get the cart data, that maps from item_id to quantity
170               my $cart = $c->session->{cart} || {};
171
172               # this is our abstract model in which items are stored
173               my $storage = $c->model("Items");
174
175               # map from item_id to item (an object or hash reference)
176               my %items = map { $_ => $storage->get_item($_) } keys %$cart;
177
178               # put the relevant info on the stash
179               $c->stash->{cart}{items} = \%items;
180               $c->stash->{cart}{quantity} = $cart;
181           }
182
183       And [a part of] the template it forwards to:
184
185           <table>
186
187               <thead>
188                   <tr>
189                       <th>Item</th>
190                       <th>Quantity</th>
191                       <th>Price</th>
192                       <th>remove</th>
193                   </tr>
194               </thead>
195
196               <tbody>
197               [%# the table body lists all the items in the cart %]
198               [% FOREACH item_id = cart.items.keys %]
199
200                   [%# each item has its own row in the table %]
201
202                   [% item = cart.items.$item_id %]
203                   [% quantity = cart.quantity.$item_id %]
204
205                   <tr>
206                       <td>
207                           [%# item.name is an attribute in the item
208                             # object, as loaded from the store %]
209                           [% item.name %]
210                       </td>
211
212                       <td>
213                           [%# supposedly this is part of a form where you
214                             # can update the quantity %]
215                           <input type="text" name="[% item_id %]_quantity"
216                               value="[% quantity %]" />
217                       </td>
218
219                       <td> $ [% item.price * quantity %] </td>
220
221                       <td>
222                           <a href="[% c.uri_for('/cart/remove') %]/[% item_id %]">
223                               <img src="/static/trash_can.png" />
224                           </a>
225                       </td>
226               [% END %]
227               <tbody>
228
229               <tfoot>
230                   <tr>
231                       <td colspan="2"> Total: </td>
232                       <td>
233                           [%# calculate sum in this cell - too
234                             # much headache for a tutorial ;-) %]
235                       </td>
236                       <td>
237                           <a href="[% c.uri_for('/cart/empty') %]">Empty cart</a>
238                       </td>
239                   </tr>
240               </tfoot>
241
242           </table>
243
244       As you can see the way that items are added into "$c->session->{cart}"
245       is pretty simple. Since "$c->session" is restored as necessary, and
246       contains data from previous requests by the same client, the cart can
247       be updated as the user navigates the site pretty transparently.
248

SECURITY ISSUES

250       These issues all relate to how session data is managed, as described
251       above.  These are not issues you should be concerned about in your
252       application code, but are here for their educational value.
253
254   (Not) Trusting the Client
255       In order to avoid the overhead of server-side data storage, the session
256       data can be included in the cookie itself.
257
258       There are two problems with this:
259
260       1.  The user can change the data.
261
262       2.  Cookies have a 4 kilobyte size limit.
263
264           The size limit is of no concern in this section, but data changing
265           is. In the database scheme the data can be trusted, since the user
266           can neither read nor write it. However, if the data is delegated to
267           the user, then special measures have to be added for ensuring data
268           integrity, and perhaps secrecy too.
269
270           This can be implemented by encrypting and signing the cookie data,
271           but this is a big headache.
272
273   Session Hijacking
274       What happens when client B says "I'm client A"?  Well, basically, the
275       server buys it. There's no real way around it.
276
277       The solution is to make "I'm client A" a difficult thing to say. This
278       is why session IDs are randomized. If they are properly randomized,
279       session IDs are so hard to guess that they must be stolen instead.
280
281       This is called session hijacking. There are several ways one might
282       hijack another user's session.
283
284       Cross Site Scripting
285
286       One is by using cross site scripting attacks to steal the cookie data.
287       In community sites, where users can cause the server to display
288       arbitrary HTML, they can use this to put JavaScript code on the server.
289
290       If the server does not enforce a strict subset of tags that may be
291       used, the malicious user could use this code to steal the cookies
292       (there is a JavaScript API that lets cookies be accessed, but this code
293       has to be run on the same website that the cookie came from).
294
295       Social Engineering
296
297       By tricking a user into revealing a URI with session data embedded in
298       it (when cookies are not used), the session ID can also be stolen.
299
300       Also, a naive user could be tricked into showing the cookie data from
301       the browser to a malicious user.
302

AUTHOR

304       Yuval Kogman <nothingmuch@woobling.org>
305
306
307
308perl v5.36.0                      2022-07C-a2t2alyst::Plugin::Session::Tutorial(3)
Impressum