From 0fe82b601b4b06ce0e9fcc5ca677810886f26717 Mon Sep 17 00:00:00 2001 From: David Cook Date: Wed, 21 Feb 2024 06:17:50 +0000 Subject: [PATCH] Bug 36084: Add CSRF token support to svc/authentication GET svc/authentication will return a CSRF token in a response header POST svc/authentication requires a CSRF token which can be sourced from the response header of GET svc/authentication or some other place like the meta element on a HTML page Note: misc/migration_tools/koha-svc.pl is a simple script which can be used to practically evaluate svc/authentication and svc/bib Signed-off-by: Jonathan Druart --- C4/Auth.pm | 2 +- misc/migration_tools/koha-svc.pl | 16 ++++++++++++++-- svc/authentication | 30 ++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/C4/Auth.pm b/C4/Auth.pm index 156626e488..d1a8235b56 100644 --- a/C4/Auth.pm +++ b/C4/Auth.pm @@ -1602,7 +1602,7 @@ sub check_api_auth { } my ( $sessionID, $session ); - unless ( $query->param('userid') ) { + unless ( $query->param('login_userid') ) { $sessionID = $query->cookie("CGISESSID"); } if ( $sessionID && not( $cas && $query->param('PT') ) ) { diff --git a/misc/migration_tools/koha-svc.pl b/misc/migration_tools/koha-svc.pl index 36e275d112..1b749d9559 100755 --- a/misc/migration_tools/koha-svc.pl +++ b/misc/migration_tools/koha-svc.pl @@ -104,7 +104,15 @@ sub new { my $ua = LWP::UserAgent->new(); $ua->cookie_jar({}); - my $resp = $ua->post( "$url/authentication", {userid =>$user, password => $password} ); + + my $get_resp = $ua->get("$url/authentication"); + my $csrf_token = $get_resp->header('X-CSRF-TOKEN'); + $self->{csrf_token} = $csrf_token; + + my $resp = $ua->post( + "$url/authentication", + { login_userid => $user, login_password => $password, csrf_token => $csrf_token } + ); die $resp->status_line unless $resp->is_success; warn "# $user $url = ", $resp->decoded_content, "\n" if $self->{debug}; @@ -140,7 +148,11 @@ sub post { my ($self,$biblionumber,$marcxml) = @_; my $url = $self->{url}; warn "# post $url/bib/$biblionumber\n" if $self->{debug}; - my $resp = $self->{ua}->post( "$url/bib/$biblionumber", 'Content_type' => 'text/xml', Content => $marcxml ); + my $csrf_token = $self->{csrf_token}; + my $resp = $self->{ua}->post( + "$url/bib/$biblionumber", 'Content_type' => 'text/xml', Content => $marcxml, + csrf_token => $csrf_token + ); die $resp->status_line unless $resp->is_success; return $resp->decoded_content; } diff --git a/svc/authentication b/svc/authentication index d375a11b01..409c2832c1 100755 --- a/svc/authentication +++ b/svc/authentication @@ -21,7 +21,7 @@ use Modern::Perl; use CGI qw ( -utf8 ); -use C4::Auth qw/check_api_auth/; +use C4::Auth qw/check_api_auth get_session/; use XML::Simple; my $query = CGI->new; @@ -41,12 +41,26 @@ my $query = CGI->new; # 3. The session cookie should not be (directly) sent back to the user's # web browser, but instead should be stored and submitted by biblios. - -my ($status, $cookie, $sessionID) = check_api_auth($query, { editcatalogue => 'edit_catalogue'} ); - -if ($status eq "ok") { - print $query->header(-type => 'text/xml', cookie => $cookie); +my $csrf_token; +my ( $status, $cookie, $sessionID ) = check_api_auth( $query, { editcatalogue => 'edit_catalogue' } ); +if ( $status eq "ok" ) { + $csrf_token = Koha::Token->new->generate_csrf( { session_id => scalar $sessionID } ); } else { - print $query->header(-type => 'text/xml'); + my $session = C4::Auth::get_session(""); + $session->param( 'ip', $session->remote_addr() ); + $session->param( 'lasttime', time() ); + $session->param( 'sessiontype', 'anon' ); + $session->param( 'interface', 'api' ); + $session->flush(); + $sessionID = $session->id(); + $cookie = $query->cookie( + -name => 'CGISESSID', + -value => $sessionID, + -HttpOnly => 1, + -secure => ( C4::Context->https_enabled() ? 1 : 0 ), + -sameSite => 'Lax' + ); + $csrf_token = Koha::Token->new->generate_csrf( { session_id => scalar $sessionID } ); } -print XMLout({ status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1); +print $query->header( -type => 'text/xml', cookie => $cookie, -'X-CSRF_TOKEN' => $csrf_token ); +print XMLout( { status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 );