Security Advisories (1)
CVE-2026-75870 (2026-08-22)

Punk versions before 0.18 for Perl allow session cookie forgery via an empty default HMAC key when a session is declared without a secret. The session keyword freezes its options onto the application as given: it does not require a secret, warn, or refuse to start when one is absent. The cookie read and the write-back both default that key to the empty string, so a declaration with no secret option, or with an undefined or empty one, signs and verifies with a zero-length HMAC-SHA256 key. An attacker who knows the cookie format can then mint one offline carrying any contents the session holds, such as a user identifier or a role. Nothing marks the misconfiguration at runtime: cookies are well formed and sessions round-trip as expected.

NAME

Punk::Session - signed cookie sessions

SYNOPSIS

package MyApp;
use Punk;

session
    secret   => secret('session_key'),   # from the secrets system
    expires  => '7d',
    samesite => 'Lax';

post '/login' => sub {
    my ($c) = @_;
    $c->session->{user_id} = $user->id;   # signed into the cookie
    $c->redirect('/');
};

get '/me' => sub {
    my ($c) = @_;
    my $id = $c->session->{user_id} or return $c->redirect('/login');
    $c->json({ id => $id });
};

post '/logout' => sub { my ($c) = @_; $c->session_expire; $c->redirect('/') };

DESCRIPTION

A session is a hashref carried in a cookie: the handler reads and writes $c->session, and at the end of the request Punk serializes it (JSON), signs it with HMAC-SHA256 and the configured secret, and sets the cookie - only when it actually changed. The client can read the contents but cannot forge them, so do not put secrets in a session; a tampered cookie is rejected and the session comes up empty.

There is no server-side store in this cut: the whole session lives in the cookie. A session that serializes over ~4KB croaks, pointing at where a server-side store would go.

THE KEYWORD

session secret => secret('key'), expires => '7d', secure => 1;

Enables sessions for the application. Options:

  • secret - the signing key (required for a real deployment); source it from the secrets system so it never sits in the code.

  • cookie - the cookie name (default punk.sid).

  • expires - a lifetime like '7d' / '12h' / '30m' / seconds; omitted means a session cookie (gone when the browser closes).

  • path (default /), domain, secure, httponly (default on), samesite (default Lax) - the cookie attributes.

It also reads from config/punk.yml under a session: block.

CONTEXT METHODS

session

The session hashref. Read and write it; it is written back to the cookie at the end of the request if it changed.

session_expire

Log out: empty the session and delete the cookie.

flash

flash_keep

One-request messages riding the session - see "FLASH".

FLASH

post '/save' => sub {
    my ($c) = @_;
    $c->flash(notice => 'Saved.');       # for the NEXT request
    $c->redirect('/list');
};

get '/list' => sub {
    my ($c) = @_;
    $c->render('list', { flash => $c->flash });
};

A flash message lives exactly one request: set it, redirect, and the redirected-to page reads it once. It rides the session under one reserved key (punk.flash), so it shares the session's machinery - the signing, the change-detected write-back, and the ~4KB cap - and needs the session keyword configured.

$c->flash(key => $value, ...) sets messages for the next request; $c->flash('key') reads one of this request's inbound messages; a bare $c->flash returns the whole inbound hashref (possibly empty) - which is also how a template gets it, passed explicitly like any other render data. $c->flash_keep re-arms the inbound messages for one more request - the redirect-through-a-redirect case.

The rotation is the session's own change detection: the first flash call of a request moves the inbound hash out of the session, so the consuming response rewrites the cookie without it, while a request that never touches flash leaves it riding untouched. Setting and reading in the same request do not meet: reads see the previous request's messages, writes feed the next one's.

SEE ALSO

Punk, "cookie" in Punk::Context, "secret" in Punk.

AUTHOR

LNATION <email@lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION <email@lnation.org>.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)