Catalyst-Plugin-CSRFToken
view release on metacpan or search on metacpan
lib/Catalyst/Plugin/CSRFToken.pm view on Meta::CPAN
=head1 NAME
Catalyst::Plugin::CSRFToken - Robust CSRF protection plugin for Catalyst
=head1 SYNOPSIS
package MyApp;
use Catalyst;
# Enable CSRF protection; requires Session plugin
__PACKAGE__->setup(qw/
Session
Session::Store::... # your choice
Session::State::Cookie # Only sane state option
CSRFToken # Add this line
/);
# Configuration
__PACKAGE__->config(
'Plugin::CSRFToken' => {
'max_age' => 3600, # Token lifespan in seconds
'default_secret' => '...', # Optional, your default secret for HMAC signing
'param_key' => '...', # Optional, default is 'csrf_token'
'single_use_csrf_token' => ..., # Optional, default is 0
'auto_check' => ..., # Optional, default is 0
},
);
If not using 'auto_check' you can enable CSRF checks on a per-action basis:
sub some_action :Local EnableCSRF {
my ($self, $c) = @_;
# CSRF check is automatically performed
}
Or manually check the token:
if($c->req->method eq 'POST') {
Catalyst::Exception->throw(message => 'csrf_token failed validation')
unless $c->check_csrf_token;
}
In your templates, specify form IDs for multiple forms:
<form id="edit_profile" method="POST">
<input type="hidden" name="csrf_token" value="[% c.csrf_token(form_id=>'edit_profile') %]">
<!-- form fields here -->
</form>
Tokens can also be provided via the 'X-CSRF-Token' HTTP request header (useful for AJAX requests):
<script
src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha384-..."
crossorigin="anonymous"
></script>
<script>
$.ajax({
url: '/some/endpoint',
type: 'POST',
headers: {
'X-CSRF-Token': '[% c.csrf_token(form_id=>"your_form_id") %]'
},
data: {
// form data here
},
success: function(response) {
// handle response
}
});
</script>
=head1 DESCRIPTION
This creates a cryptographical token tied to a given web session used for CSRF protection. You can
generate a token and pass it to your view layer where it should be added to the form you are
trying to process, typically as a hidden field called 'csrf_token' (although you can change
that in configuration if needed).
The value returned by C<csrf_token> is an opaque, masked representation of
the token held in the session. A fresh random mask is generated on every
call, so repeated calls return different strings while all remain valid for
the same session token. This prevents the stable response secret required
by BREACH compression-oracle attacks. Applications must not parse or alter
the returned value.
All POST, PUT, DELETE, and PATCH requests are automatically checked for a valid CSRF token when
'auto_check_csrf_token' is enabled. If the check fails, a 403 Forbidden response is returned. The
response can be customized by overriding the 'delegate_failed_csrf_token_check' method or as
otherwise documented below.
If you leave this disabled, you will need to manually check the token using the 'check_csrf_token'
method. Example:
if($c->req->method eq 'POST') {
Catalyst::Exception->throw(message => 'csrf_token failed validation')
unless $c->check_csrf_token;
}
Or you can enable CSRF checks on a per-action basis by adding the 'EnableCSRF' attribute to the
action. Example:
sub some_action :Local EnableCSRF {
my ($self, $c) = @_;
# CSRF check is automatically performed
}
=head2 Version 1.100 Notes
This version changes the on-the-wire token format and does not accept tokens
issued by version 1.001 or earlier. Read this before you deploy.
Version 1.001 returned the same token representation for a given session and
form until it expired. In an application that reflects attacker-controlled
input into a compressed response containing that token, the stable value can
become the secret in a BREACH compression oracle. Tokens are now masked with
fresh randomness on every call, so the representation differs each time while
still validating against the same session token.
( run in 1.972 second using v1.01-cache-2.11-cpan-f03e8824b8d )