Mojolicious-Plugin-WithCSRFProtection
view release on metacpan or search on metacpan
# SYNOPSIS
# in a lite application
post '/some-url' => ( with_csrf_protection => 1 ) => sub { ... };
# in a full application
$app->routes->post('/some-url')
->with_csrf_protection
->to(...);
# DESCRIPTION
This Mojolicious plugin provides a routing condition (called
`with_csrf_protection`) and routing shortcut to add that condition (also called
`with_csrf_protection`) that can be used to protect against cross site request
forgery.
Adding the condition to the route checks a valid CSRF token was passed, either
in the `X-CSRF-Token` HTTP header or in the `crsf_token` parameter.
Failing the CSRF check causes a 403 error and the `bad_csrf` template to be
rendered, or if no such template is found a simple error string to be
output. This behavior is unlike most conditions that can be applied to
Mojolicious routes that normally just cause the route matching to fail and
alternative subsequent routes to be evaluated, but immediately returning an
error response makes sense for a failed CSRF check. The actual error rendering
is performed by the `reply.bad_csrf` helper that this plugin installs, and if
you want different error output you should override that helper.
# EXAMPLES
## A Mojolicious::Lite application
Here's a simple Mojolicious application that I can run on my desktop computer
that creates a very simple web interface to adding things to do to my
`todo.txt`.
Because I don't want anyone web page on the internet to be able to tell my
browser to add whatever that web page feels like to my todo list, I add CSRF
protection with the `with_csrf_protection => 1` condition to the POST.
#!/usr/bin/perl
use Mojolicious::Lite;
plugin 'WithCSRFProtection';
plugin 'TagHelpers';
get '/' => sub {} => 'index';
post '/note' => (with_csrf_protection => 1) => sub {
my ($c) = @_;
open my $fh, '>>', $ENV{HOME}.'/todo.txt' or die "Can't open todo: $!";
print $fh $c->param('item'), "\n";
};
app->start;
__DATA__
@@ index.html.ep
<html>
<body>
%= form_for note => begin
%= text_field 'item'
%= csrf_field
%= submit_button
% end
</body>
</html>
@@ note.html.ep
<html>
<body>
Okay, I wrote that down!
</body>
</html>
The template for the index makes use of the `csrf_field` tag helper to
render a hidden input field containing the current csrf\_token:
<html>
<body>
<form action="/note" method="POST">
<input name="item" type="text">
<input name="csrf_token" type="hidden" value="428d33ed67f886dd1a2c1a3c493708f5158bf77d">
<input type="submit" value="Ok">
</form></body>
</html>
However if a bad agent causes your browser to try POSTing to the form without
the CSRF token (or for that matter the corresponding session cookie), you just
get the standard CSRF protection error message:
shell$ curl -X POST -F 'item=transfer money to bad guys' http://127.0.0.1:3000/note
( run in 1.762 second using v1.01-cache-2.11-cpan-39bf76dae61 )