Cataly...utorial(3pm)
π¦ Catalyst::Plugin::Session::Tutorial(3pm) β the manual page, but make it make sense
okay bestie. this is Catalyst::Plugin::Session::Tutorial(3pm) from the User Contributed Perl Documentation, and it's literally just here to help you understand and use sessions. sit down, grab a strawberry π, we're doing this together.
π NAME π
Catalyst::Plugin::Session::Tutorial β Understanding and using sessions.
like. the whole thing. sessions. understanding them. using them. that's the vibe. π
β¨ ASSUMPTIONS β¨
before we go any further, this tutorial is assuming some stuff about you, and honestly? fair. it assumes:
- you're already familiar with web applications in general,
- you know Catalyst specifically β and like, up to models and configuration level, not just the hello-world,
- and you know what HTTP is.
if that's you, slay, keep reading. if it's not, no shade, go do those first, this page will still be here. π«Ά
π WHAT ARE SESSIONS π
okay so picture this. users are using a site. and it's the kind of site that knows who they are β you know, sites you log in to, sites that let you keep a shopping cart, that whole genre. and here's the problem: the server that's preparing the content has to know that request X is coming from client A while request Y is coming from client B, so that each user gets their content and not somebody else's. π
and the issue is... HTTP is a stateless protocol. which means every single request is distinct, and even if it's coming from the same client, it's difficult to know that. it's giving "who are you again?" every time. π
so the way sessions are maintained between distinct requests is that the client just says, for every request, "I'm client A" or "I'm client B". iconic behavior honestly.
and this piece of data that tells the server "I'm X"? that's called the session ID, and the threading of several requests together is called a session. periodt. π
π₯ HOW SESSIONS WORK π₯
πͺ Cookies πͺ
HTTP has a feature that makes all of this easier, and it's called cookies. a cookie is something the server asks the client to save somewhere, and then resend every time a request is made. lowkey genius.
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. and that's it. that's the whole trick.
π§ Cookie Alternatives π§
another way to make sure the session ID gets repeated is to just include it in every URI. no cookies, no problem.
this can be as either a part of the path, or as a query parameter.
β οΈ okay but this technique has several issues which are discussed in "CAVEATS" in Catalyst::Plugin::Session::State::URI. go read that before you commit to URI sessions, bestie, i'm being so serious.
π₯οΈ 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, that database can contain a shopping cart's contents, user preferences, etc. it's giving β¨organizedβ¨.
π USING SESSIONS π
in Catalyst, the Catalyst::Plugin::Session plugin provides an API for convenient handling of session data. and fun fact, this API is based on the older, less flexible and less reliable Catalyst::Plugin::Session::FastMmap. so like... we've evolved. we've grown. growth. π±
the plugin is modular, meaning it requires backend plugins to be used. you can't just raw-dog it, you need your backends. π
π State Plugins π
state plugins handle session ID persistence. for example, Catalyst::Plugin::Session::State::Cookie creates a cookie with the session ID in it. classic.
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. it's automatic. it's hands-off. it's the dream. π
ποΈ 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. pick your fighter. π₯
βοΈ 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. iykyk.
each backend plugin requires its own configuration options (with most plugins providing sensible defaults, because they're thoughtful like that). 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",
});
and yes, my_fabulous_cookie is a real example name and i love that for us. πͺβ¨
π Usage π
now, let's say we have an online shop, and the user is adding an item to the shopping cart. classic. iconic. everyday behavior.
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";
}
}
so the way this works is that $c->session always returns a hash reference to some data which is stored by the storage backend plugin. and the hash reference returned always contains the same items that were in there at the end of the last request. memory? she has it. π
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. efficient. we love her. β¨
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. brand new baby session. πΌ
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. the β¨continuityβ¨ of it all.
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>
ποΈ yes that's a trash can png for the remove link. we love a themed asset. π
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. it just works. no cap. πΈ
girl help this page is long, so it comes in 2 parts and this was part 1 β more parts follow, don't go anywhere bestie π
π¨ SECURITY ISSUES π¨
Okay bestie, these ones are all about how session data gets managed, like we talked about up above. Real talk: these are not the kind of thing you stress about in your application code, they're here purely for the educational vibes. Think of it as lore. β¨
π (Not) Trusting the Client
So the whole reason someone might wanna put session data inside the cookie itself is to dodge the overhead of storing it server-side. Understandable! Efficient! Also kind of a trap. π
There are two problems with this:
- The user can change the data. (Not me trusting the client π)
- Cookies have a
4 kilobytesize limit.
The size limit is not the issue in this section, the data changing is. In the database scheme the data can be trusted, because the user can neither read it nor write it. But if you delegate the data to the user, then you have to add special measures to make sure the data stays intact β and maybe keep it secret too. π
You can pull this off by encrypting and signing the cookie data, but honestly? It's a big headache. π«
π΅οΈ Session Hijacking
Okay so what happens when client B just walks in and says "I'm client A"? Well. Basically the server just buys it. There's no real way around that. That's it, that's the whole vibe. π
So the solution is to make saying "I'm client A" genuinely hard. That's literally why session IDs are randomized. If they're properly randomized, session IDs are so hard to guess that a hater has to steal them instead of guessing. β¨
And that right there is what we call session hijacking. There are several ways someone might hijack another user's session:
π» Cross Site Scripting
One way is using cross site scripting attacks to steal the cookie data. On community sites, where users can make the server display arbitrary HTML, they can use that to put JavaScript code on the server. π§
And if the server doesn't enforce a strict subset of tags that may be used, that malicious user could use this code to steal the cookies (there's a JavaScript API that lets cookies be accessed, but this code has to be run on the same website that the cookie came from). No cap. π₯
π Social Engineering
By tricking a user into revealing a URI with session data embedded in it (this is the when-cookies-are-not-used situation, iykyk), 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. Protect your friends, bestie. π«Ά
π AUTHOR π
Yuval Kogman <nothingmuch@woobling.org>
And that's a wrap on the whole tutorial, bestie!! π This page came to you courtesy of the Cataly...utorial(3pm) manual, perl v5.40.1, dated 2025-08-10, from User Contributed Perl Documentation. Go build something iconic with your sessions, and stay safe out there. Slay, periodt. π
ππ¦
π¦ where this page comes from
- ubuntu resolute: libcatalyst-plugin-session-perl 0.44-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libcatalyst-plugin-session-perl 0.44-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms