1 package Koha::Middleware::CSRF;
3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use parent qw(Plack::Middleware);
24 my ( $self, $env ) = @_;
25 my $req = Plack::Request->new($env);
27 my %stateless_methods = (
34 my %stateful_methods = (
41 my $original_op = $req->param('op');
42 my $request_method = $req->method // q{};
44 if ( $stateless_methods{$request_method} && defined $original_op && $original_op =~ m{^cud-} ) {
45 $error = sprintf "Programming error - op '%s' must not start with 'cud-' for %s", $original_op,
47 } elsif ( $stateful_methods{$request_method} ) {
49 # Get the CSRF token from the param list or the header
50 my $csrf_token = $req->param('csrf_token') || $req->header('HTTP_CSRF_TOKEN');
52 if ( defined $req->param('op') && $original_op !~ m{^cud-} ) {
53 $error = sprintf "Programming error - op '%s' must start with 'cud-' for %s", $original_op,
55 } elsif ( !$csrf_token ) {
56 $error = sprintf "Programming error - No CSRF token passed for %s", $request_method;
59 Koha::Token->new->check_csrf(
61 session_id => scalar $req->cookies->{CGISESSID},
67 $error = "wrong_csrf_token";
72 #NOTE: It is essential to check for this environmental variable.
73 #NOTE: If we don't check for it, then we'll also throw an error for the "subrequest" that ErrorDocument uses to
74 #fetch the error page document. Then we'll wind up with a very ugly error page and not our pretty one.
75 if ( $error && !$env->{'plack.middleware.Koha.CSRF'} ) {
77 #NOTE: Other Middleware will take care of logging to correct place, as Koha::Logger doesn't know where to go here
79 $env->{'plack.middleware.Koha.CSRF'} = "BAD_CSRF";
80 my $res = Plack::Response->new( 403, [ 'Content-Type' => 'text/plain' ], ["Bad CSRF"] );
81 return $res->finalize;
84 return $self->app->($env);