Mojolicious-Plugin-CSRFDefender
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/CSRFDefender.pm view on Meta::CPAN
package Mojolicious::Plugin::CSRFDefender;
use strict;
use warnings;
use Carp;
our $VERSION = '0.0.8';
use base qw(Mojolicious::Plugin Class::Accessor::Fast);
__PACKAGE__->mk_accessors(qw(
parameter_name
session_key
token_length
error_status
error_content
error_template
onetime
));
use String::Random;
use Path::Class;
sub register {
my ($self, $app, $conf) = @_;
# Plugin config
$conf ||= {};
# setting
$self->parameter_name($conf->{parameter_name} || 'csrftoken');
$self->session_key($conf->{session_key} || 'csrftoken');
$self->token_length($conf->{token_length} || 32);
$self->error_status($conf->{error_status} || 403);
$self->error_content($conf->{error_content} || 'Forbidden');
$self->onetime($conf->{onetime} || 0);
if ($conf->{error_template}) {
my $file = $app->home->rel_file($conf->{error_template});
$self->error_template($file);
}
# input check
$app->hook(before_dispatch => sub {
my ($c) = @_;
unless ($self->_validate_csrf($c)) {
my $content;
if ($self->error_template) {
my $file = file($self->error_template);
$content = $file->slurp;
}
else {
$content = $self->{error_content},
}
$c->render(
status => $self->{error_status},
text => $content,
);
};
});
# output filter
$app->hook(after_dispatch => sub {
my ($c) = @_;
my $token = $self->_get_csrf_token($c);
my $p_name = $self->parameter_name;
my $body = $c->res->body;
$body =~ s{(<form\s*[^>]*method=["']POST["'][^>]*>)}{$1\n<input type="hidden" name="$p_name" value="$token" />}isg;
$c->res->body($body);
});
return $self;
}
sub _validate_csrf {
my ($self, $c) = @_;
my $p_name = $self->parameter_name;
my $s_name = $self->session_key;
my $request_token = $c->req->param($p_name);
my $session_token = $c->session($s_name);
if ($c->req->method eq 'POST') {
return 0 unless $request_token;
return 0 unless $session_token;
return 0 unless $request_token eq $session_token;
}
# onetime
if ($c->req->method eq 'POST' && $self->onetime) {
$c->session($self->{session_key} => '');
}
return 1;
}
sub _get_csrf_token {
my ($self, $c) = @_;
my $key = $self->session_key;
my $token = $c->session($key);
my $length = $self->token_length;
return $token if $token;
$token = String::Random::random_regex("[a-zA-Z0-9_]{$length}");
$c->session($key => $token);
return $token;
}
1;
__END__
=head1 NAME
Mojolicious::Plugin::CSRFDefender - Defend CSRF automatically in Mojolicious Application
=head1 VERSION
This document describes Mojolicious::Plugin::CSRFDefender.
=head1 SYNOPSIS
# Mojolicious
$self->plugin('Mojolicious::Plugin::CSRFDefender');
# Mojolicious::Lite
plugin 'Mojolicious::Plugin::CSRFDefender';
=head1 DESCRIPTION
This plugin defends CSRF automatically in Mojolicious Application.
Following is the strategy.
( run in 1.330 second using v1.01-cache-2.11-cpan-39bf76dae61 )