Bug 36598

Summary: Add CSRF protection to Mojolicious apps
Product: Koha Reporter: Julian Maurice <julian.maurice>
Component: Architecture, internals, and plumbingAssignee: Julian Maurice <julian.maurice>
Status: RESOLVED FIXED QA Contact: Marcel de Rooy <m.de.rooy>
Severity: major    
Priority: P5 - low CC: dcook, didier.gautheron, fridolin.somers, jonathan.druart, laurent.ducos, lisette, lucas, m.de.rooy, martin.renvoize, nick, pedro.amorim, philippe.blouin, tomascohen, victor, wainuiwitikapark
Version: Main   
Hardware: All   
OS: All   
Change sponsored?: --- Patch complexity: Small patch
Documentation contact: Documentation submission:
Text to go in the release notes:
Version(s) released in:
24.11.00,24.05.04
Circulation function:
Bug Depends on: 36192, 36700    
Bug Blocks:    
Attachments: Bug 36598: Enable CSRF protection for Mojolicious apps
Bug 36598: Enable CSRF protection for Mojolicious apps
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)
Bug 36598: Enable CSRF protection for Mojolicious apps
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)
Bug 36598: Fix CSRF header name (underscore -> hyphen)
Bug 36598: Improve documentation and error message in CSRF plugin
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter
Bug 36598: Add comments asking to keep both CSRF checks in sync
Bug 36598: Enable CSRF protection for Mojolicious apps
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)
Bug 36598: Fix CSRF header name (underscore -> hyphen)
Bug 36598: Improve documentation and error message in CSRF plugin
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter
Bug 36598: Add comments asking to keep both CSRF checks in sync
Bug 36598: (QA follow-up): tidy up code
Bug 36598: Enable CSRF protection for Mojolicious apps
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)
Bug 36598: Fix CSRF header name (underscore -> hyphen)
Bug 36598: Improve documentation and error message in CSRF plugin
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter
Bug 36598: Add comments asking to keep both CSRF checks in sync
Bug 36598: (QA follow-up): tidy up code

Description Julian Maurice 2024-04-12 13:18:51 UTC

    
Comment 1 Julian Maurice 2024-04-12 13:19:24 UTC
Created attachment 164847 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Test plan:
1. Run bin/opac daemon -l http://*:3001/
2. Go to http://localhost:3001/cgi-bin/koha/opac-user.pl
3. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
4. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
5. Reload the page, try to log in normally without modifying the DOM, it
   should succeed
6. Run bin/intranet daemon -l http://*:3002/
7. Go to http://localhost:3002/cgi-bin/koha/mainpage.pl
8. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
9. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
10. Reload the page, try to log in normally without modifying the DOM,
    it should succeed
11. Run prove t/db_dependent/mojo/csrf.t
Comment 2 Julian Maurice 2024-04-12 13:37:58 UTC
Created attachment 164848 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Test plan:
1. Run bin/opac daemon -l http://*:3001/
2. Go to http://localhost:3001/cgi-bin/koha/opac-user.pl
3. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
4. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
5. Reload the page, try to log in normally without modifying the DOM, it
   should succeed
6. Run bin/intranet daemon -l http://*:3002/
7. Go to http://localhost:3002/cgi-bin/koha/mainpage.pl
8. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
9. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
10. Reload the page, try to log in normally without modifying the DOM,
    it should succeed
11. Run prove t/db_dependent/mojo/csrf.t
Comment 3 David Cook 2024-04-14 23:19:54 UTC
Looks pretty good at a glance. I'd need a bit more time with it, but this should work.
Comment 4 Julian Maurice 2024-04-15 06:45:00 UTC
Setting status to ASSIGNED. It needs more work (adding the "cud-" prefix check)
Comment 5 David Cook 2024-04-15 07:07:02 UTC
(In reply to Julian Maurice from comment #4)
> Setting status to ASSIGNED. It needs more work (adding the "cud-" prefix
> check)

Ah right. Yes, we wouldn't need it for the API, but would more broadly I suppose...
Comment 6 Julian Maurice 2024-04-15 07:14:30 UTC
Created attachment 164870 [details] [review]
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)
Comment 7 Matt Blenkinsop 2024-04-18 10:33:53 UTC
Created attachment 165075 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Test plan:
1. Run bin/opac daemon -l http://*:3001/
2. Go to http://localhost:3001/cgi-bin/koha/opac-user.pl
3. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
4. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
5. Reload the page, try to log in normally without modifying the DOM, it
   should succeed
6. Run bin/intranet daemon -l http://*:3002/
7. Go to http://localhost:3002/cgi-bin/koha/mainpage.pl
8. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
9. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
10. Reload the page, try to log in normally without modifying the DOM,
    it should succeed
11. Run prove t/db_dependent/mojo/csrf.t

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Comment 8 Matt Blenkinsop 2024-04-18 10:33:56 UTC
Created attachment 165076 [details] [review]
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Comment 9 Julian Maurice 2024-05-03 07:18:01 UTC
Created attachment 166094 [details] [review]
Bug 36598: Fix CSRF header name (underscore -> hyphen)
Comment 10 Victor Grousset/tuxayo 2024-05-12 00:34:40 UTC
Comment on attachment 165075 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Review of attachment 165075 [details] [review]:
-----------------------------------------------------------------

::: Koha/App/Plugin/CSRF.pm
@@ +45,5 @@
> +
> +Defines an `around_action` hook that will return a 403 response if CSRF token
> +is missing or invalid.
> +
> +This verification occurs only for HTTP methods POST, PUT, DELETE and PATCH.

The commit "Prohibit CUD operations with safe HTTP methods" doesn't update this.
Comment 11 Victor Grousset/tuxayo 2024-05-12 00:34:52 UTC
Comment on attachment 165076 [details] [review]
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)

Review of attachment 165076 [details] [review]:
-----------------------------------------------------------------

::: Koha/App/Plugin/CSRF.pm
@@ +64,5 @@
>              my $method = $c->req->method;
> +            if ( $method eq 'GET' || $method eq 'HEAD' || $method eq 'OPTIONS' || $method eq 'TRACE' ) {
> +                my $op = $c->req->param('op');
> +                if ( $op && $op =~ /^cud-/ ) {
> +                    return $c->reply->exception('Wrong HTTP method')->rendered(400);

This will be confusing without basic explanation about the op cud- prefix depending on whether it's a stateful or stateless HTTP method. When helping someone in another ticket, it was the op that was wrong, not the method.

What about something like «The `op` param starting with `cud-` goes only with a stateful HTTP method. Either the method or the `op` param is wrong.»
(rephrasing welcome)
Comment 12 Victor Grousset/tuxayo 2024-05-12 00:43:27 UTC
testing notes:

> 1. Run bin/opac daemon -l http://*:3001/

To use in koha-testing-docker, I did
sudo killall apache2 
# `service apache2 stop` had the expected output but apache was still running...
bin/opac daemon -l http://*:8080/  # so it's a port mapped to the host


---


Are those errors expected?:

prove t/db_dependent/mojo/csrf.t
t/db_dependent/mojo/csrf.t .. File not found : js/desk_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.
File not found : js/register_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.
File not found : js/desk_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.
File not found : js/register_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.
File not found : js/desk_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.
File not found : js/register_selection.js at /kohadevbox/koha/Koha/Template/Plugin/Asset.pm line 84.

That looks like mojo implementation having something incomplete in assets loading (only in the context of tests?) and not related to this ticket. Since they are still popping if I comment the usage of the CSRF plugin.
Comment 13 Julian Maurice 2024-05-13 12:53:04 UTC
(In reply to Victor Grousset/tuxayo from comment #12) 
> Are those errors expected?:
> [...]
> That looks like mojo implementation having something incomplete in assets
> loading (only in the context of tests?) and not related to this ticket.
> Since they are still popping if I comment the usage of the CSRF plugin.
This is not related to mojo nor this bug. See bug 36582 comment 18
Comment 14 Julian Maurice 2024-05-13 13:07:12 UTC
Created attachment 166634 [details] [review]
Bug 36598: Improve documentation and error message in CSRF plugin
Comment 15 Victor Grousset/tuxayo 2024-05-14 01:28:23 UTC
Thanks for the changes :)

Rechecking against the plack middleware (Koha/Middleware/CSRF.pm) I noticed that it also errors when with a stateful method, the op is lacking the `cud-` prefix. Which the mojo plugin here doesn't enforce.

Another finding (less clear, at least in what to do):
In the end, there will be duplication of the logic in the plack middleware and the mojo plugin. That logic doesn't look complex enough or enough likely to change much in the future to be worth refactoring. Maybe just add a comment at the start of the plack middleware logic that it must be kept in sync the with mojo plugin? Because any change in the middleware will very likely forget mojo. I'm not sure what the best way to handle that case.
Comment 16 Julian Maurice 2024-05-14 07:22:50 UTC
Created attachment 166673 [details] [review]
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter
Comment 17 Julian Maurice 2024-05-14 07:34:14 UTC
(In reply to Victor Grousset/tuxayo from comment #15)
> Another finding (less clear, at least in what to do):
> In the end, there will be duplication of the logic in the plack middleware
> and the mojo plugin. That logic doesn't look complex enough or enough likely
> to change much in the future to be worth refactoring.
I agree.

> Maybe just add a
> comment at the start of the plack middleware logic that it must be kept in
> sync the with mojo plugin? Because any change in the middleware will very
> likely forget mojo. I'm not sure what the best way to handle that case.
Ditch Plack/Starman in favor of Mojo/Hypnotoad (so there is no need for duplication) ? :D

I'm not sure if a comment can help, but it is worth a try
Comment 18 Julian Maurice 2024-05-14 07:35:48 UTC
Created attachment 166675 [details] [review]
Bug 36598: Add comments asking to keep both CSRF checks in sync
Comment 19 Victor Grousset/tuxayo 2024-05-14 18:40:18 UTC
Created attachment 166727 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Test plan:
1. Run bin/opac daemon -l http://*:3001/
2. Go to http://localhost:3001/cgi-bin/koha/opac-user.pl
3. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
4. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
5. Reload the page, try to log in normally without modifying the DOM, it
   should succeed
6. Run bin/intranet daemon -l http://*:3002/
7. Go to http://localhost:3002/cgi-bin/koha/mainpage.pl
8. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
9. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
10. Reload the page, try to log in normally without modifying the DOM,
    it should succeed
11. Run prove t/db_dependent/mojo/csrf.t

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 20 Victor Grousset/tuxayo 2024-05-14 18:40:21 UTC
Created attachment 166728 [details] [review]
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 21 Victor Grousset/tuxayo 2024-05-14 18:40:24 UTC
Created attachment 166729 [details] [review]
Bug 36598: Fix CSRF header name (underscore -> hyphen)

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 22 Victor Grousset/tuxayo 2024-05-14 18:40:28 UTC
Created attachment 166730 [details] [review]
Bug 36598: Improve documentation and error message in CSRF plugin

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 23 Victor Grousset/tuxayo 2024-05-14 18:40:31 UTC
Created attachment 166731 [details] [review]
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 24 Victor Grousset/tuxayo 2024-05-14 18:40:35 UTC
Created attachment 166732 [details] [review]
Bug 36598: Add comments asking to keep both CSRF checks in sync

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>
Comment 25 Victor Grousset/tuxayo 2024-05-14 18:40:38 UTC
Created attachment 166733 [details] [review]
Bug 36598: (QA follow-up): tidy up code
Comment 26 Victor Grousset/tuxayo 2024-05-14 18:45:58 UTC
> Ditch Plack/Starman in favor of Mojo/Hypnotoad
> (so there is no need for duplication) ? :D
﷐[U+1F638]﷑
Comment 27 Victor Grousset/tuxayo 2024-05-14 19:23:18 UTC
Seems to work as expected.
Also tried this scenario:
1. open the opac form to edit one's detail
2. log out and log in from another tab
3. post the form
4. wrong CSRF token error as expected
5. same on the staff interface with the login form

Messing with the op hidden field value to add or remove cud- gives the expected result.

Code looks good, complies with the guidelines. I'm just not sure enough there isn't a blind spot somewhere since I didn't follow CSRF introduction. So if someone else can just confirm that, we can move forward.
Comment 28 Marcel de Rooy 2024-09-06 07:40:19 UTC
Created attachment 171119 [details] [review]
Bug 36598: Enable CSRF protection for Mojolicious apps

Test plan:
1. Run bin/opac daemon -l http://*:3001/
2. Go to http://localhost:3001/cgi-bin/koha/opac-user.pl
3. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
4. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
5. Reload the page, try to log in normally without modifying the DOM, it
   should succeed
6. Run bin/intranet daemon -l http://*:3002/
7. Go to http://localhost:3002/cgi-bin/koha/mainpage.pl
8. With browser devtools, locate csrf_token hidden input within the
   login form and remove it or modify it
9. Try to submit the form with correct credentials, it should fail
   ("Wrong CSRF token")
10. Reload the page, try to log in normally without modifying the DOM,
    it should succeed
11. Run prove t/db_dependent/mojo/csrf.t

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 29 Marcel de Rooy 2024-09-06 07:40:21 UTC
Created attachment 171120 [details] [review]
Bug 36598: Prohibit CUD operations with safe HTTP methods (GET/HEAD/...)

Signed-off-by: Matt Blenkinsop <matt.blenkinsop@ptfs-europe.com>
Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 30 Marcel de Rooy 2024-09-06 07:40:24 UTC
Created attachment 171121 [details] [review]
Bug 36598: Fix CSRF header name (underscore -> hyphen)

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 31 Marcel de Rooy 2024-09-06 07:40:27 UTC
Created attachment 171122 [details] [review]
Bug 36598: Improve documentation and error message in CSRF plugin

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 32 Marcel de Rooy 2024-09-06 07:40:30 UTC
Created attachment 171123 [details] [review]
Bug 36598: Prevent use of unsafe HTTP method with non-cud op parameter

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 33 Marcel de Rooy 2024-09-06 07:40:33 UTC
Created attachment 171124 [details] [review]
Bug 36598: Add comments asking to keep both CSRF checks in sync

Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 34 Marcel de Rooy 2024-09-06 07:40:36 UTC
Created attachment 171125 [details] [review]
Bug 36598: (QA follow-up): tidy up code

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Comment 35 Wainui Witika-Park 2024-09-12 00:02:45 UTC
Hi I am having trouble with testing this on 23.05

I run the following:

kohadev-koha@kohadevbox:koha((ef654af7624...))$ bin/opac daemon -l http://*:3001/
[2024-09-11 23:59:24.10736] [22804] [info] Listening at "http://*:3001/"
Web application available at http://127.0.0.1:3001/

But then http://127.0.0.1:3001/ is unable to connect

Can I please get some help with testing this on ktd?
Comment 36 Julian Maurice 2024-09-12 06:17:29 UTC
(In reply to wainuiwitikapark from comment #35)
> Can I please get some help with testing this on ktd?

I'm not a user of ktd but you probably need to replace 127.0.0.1 by the container IP address
Comment 37 Victor Grousset/tuxayo 2024-09-20 04:43:48 UTC
(In reply to wainuiwitikapark from comment #35)
> Hi I am having trouble with testing this on 23.05

The bug dependencies for this aren't on 23.05. So it shouldn't be backported.

----

As for testing on other branches that need this. I think I changed the port from 3001 to 8081. And before starting with Mojolicious I killed apache since it was already the port. (That's the lazy way)
Comment 38 Fridolin Somers 2024-09-20 07:29:01 UTC
This is only for 24.11 and 24.05 right ?
Comment 39 Julian Maurice 2024-09-20 07:53:58 UTC
(In reply to Fridolin Somers from comment #38)
> This is only for 24.11 and 24.05 right ?
Yes
Comment 40 Wainui Witika-Park 2024-09-23 05:14:59 UTC
Thanks - not needed for 23.05 so not backporting to 23.05

:)
Comment 41 Lucas Gass (lukeg) 2024-10-09 14:36:28 UTC
Backported to 24.05.x for 24.05.04
Comment 42 Katrin Fischer 2024-10-18 10:17:13 UTC
Pushed for 24.11!

Well done everyone, thank you!