Support for CSRF token handling in the REST framework should be improved to reduce the amount of boiler plate code required in handlers. Also, we should protect against handlers forgetting about the need for CSRF protection.
Currently, handlers that should protect against CSRF must:
- either override requireSafeAgainstCsrf() to return true, to disallow cookie based auth.
- or use the TokenAwareHandlerTrait to implement token based CSRF protection.
Handlers that use TokenAwareHandlerTrait currently must add some boiler plate code in order to work:
- override the validate() method to include a call to the validateToken() method provided by TokenAwareHandlerTrait.
- override getBodyParamSettings() to include a call to the getTokenParamDefinition() method provided by TokenAwareHandlerTrait.
Handlers for POST or PUT or DELETE that fail to do any of the above will be vulnerable against CSRF attacks.
Option 1
Better integrate TokenAwareHandlerTrait with the Handler base class.
- Introduce a validateToken (or similar) method into the Handler base class, and make TokenAwareHandlerTrait provide an implementation for that method. The default implementation in the base class will throw a LogicException.
- Let the base class detect if CSRF protection is needed. This is the case if the request is not using a "safe" method (GET or HEAD) and needsWriteAccess() returns true.
This way, vulnerable handlers that forget to implement CSRF protection will fail with a LogicException. And handlers that do implement CSRF token handling no longer need to override the validate() method, though they still have to call getTokenParamDefinition() from getBodyParamSettings().
Handlers can control the name of the token parameter by overriding the getToken() method.
Experimental patch: https://gerrit.wikimedia.org/r/c/mediawiki/core/+/1034457
Option 2
Make CSRF token handling automatic in the Handler base class.
The Handler base class can detect if CSRF tokens are needed (see above). If so, it can construct a helper object, CsrfTokenHandler or some such, that implements token validation. getBodyParamSettings() will automatically return a param spec for the token parameter by calling getTokenParamDefinition(), which would also be added to the base class. Handler classes that implement getBodyParamSettings() will have to call the parent implementation.
This way, handlers don't have to know or care about CSRF protection at all. It just happens.
Option 3
Make CSRF token middleware
If we introduce support for middleware into the REST framework, CSRF token handling can be implemented in a middleware component that can be applied to endpoints by declaring it in the route definition.
This way, the Handler base class as well as concrete handlers could be entirely unaware of CSRF tokens and session handling. However, there would be no protection against handlers failing to declare the necessary middleware. Unless perhaps the CSRF protection middleware would be added automatically to handlers for methods other than GET and HEAD.
The name of the token parameter could be configurable in the middleware, but that would be in the route definition. The handler itself would be unaware of the additional parameter. It's not clear if and how middleware components could declare parameters (resp. body fields).