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).

All POST, PUT, 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 'csrf_failure_response' 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.001 Notes

Older versions of this plugin contained security and related bugs stemming from a 
mistake I made in the first release.   Over the years I've tried to tweak it to
make it more secure and robust.  However, I've come to the conclusion that the
best way to fix the issue required me to substantially rewrite the guts.  I did
my best to maintain the public API and as much of the private API as I could, but
its possible this version break compatibility with older versions.  Usually I
try to avoid this, but in this case I felt it was necessary because I think the
old versions are insecure and you should not use them in any case.  Hit me with a
bug report if you find something that doesn't work as expected and I will try to
fix it, if I can without reintroducing the security issues.

This version also adds more debugging log output when Catalyst is run in debug
mode.  This should help you understand what is going on with the CSRF token
generation and validation.   But the log is more noisy.

=head1 CONFIGURATION



( run in 1.506 second using v1.01-cache-2.11-cpan-524268b4103 )