Form protection problems in CakePHP 4

We are currently in the process of updating a large (i.e. several hundreds of thousands of non-framework lines of code) and heavily-visited production website from CakePHP 3.2 to CakePHP 4.3.

The website makes extensive use of CakePHP’s form protection functionality. One change that occurred between versions 3.2 and 4.3 is that the form protector now uses the session ID to generate the token hash which is included as a hidden field in forms (see e.g. this commit). The commit message indicates that user-specific data was added to the token hash “to avoid privilege escalation”. I presume that this is intended to address the issue where two users share a computer: if the first user logs out but accidentally leaves a tab open with a form, a second user could submit that form, since the form protection hash was not unique to the first user.

A side effect of is is that, if a user is required to reauthenticate as a result of a session timeout (or an explicit logout), all existing forms now become invalid, unlike in Cake 3. This causes major headaches for us because we wish to continue to allow users to submit existing forms (in the both the current tab and other tabs) following a reauthentication (in order not to lose work) and, to this end, the project also has JavaScript component to automatically resubmit failed Ajax post requests following reauthentication. All of this functionality is now broken.

We have considered a number of possible solutions, the simplest of which seems to be reverting the hash logic to the CakePHP 3 position and handling the possibility of privilege escalation via explicit privilege checks on all actions (which have already been in place for a long time). However, it seems that even this approach is not possible, as CakePHP does not provide any way to override or modify the behaviour of the generateHash() method in the FormProtector class (which is explicitly instantiated via the ‘new’ keyword in the FormHelper and FormProtectionComponent (NB: link not added owing to 2-link per-post limit) classes.

Does anyone have any suggestions as to handle this issue so as to (i) achieve the goal of incorporating form protection while (ii) allowing users to be able to submit existing forms following reauthentication?

If there is no way to do so, would the CakePHP team consider a future change to allow more flexibility in the logic used to generate the form protection hash?

I assume increasing the session timeout isn’t viable? Not even sure that would resolve it.

@Jawfin - Thanks for this. Yes, increasing session time decreases the likelihood of the issue occurring, but it’s still an occasional issue and it is not appreciated by customers when work is lost!

As my original post was initially blocked by the Discourse spam filter, I also asked this question on the CakePHP Slack channel and received the following input:

  • Allowing form re-submissions after session timeout is generally a bad thing - see p.22 of this security audit of the CakePHP framework: https://wiki.mozilla.org/images/4/40/Cakephp-report.pdf. However, in our case, we check the user’s privileges explicitly on every call, so this risk is addressed in a different way.
  • There’s no easy way to override the _generateHash method in FormProtector.php, so the best option is to extend FormHelper and FormProtectorComponent and override the relevant methods which create the protector instance.
  • It is possible to alias your helper and component so that you can continue using e.g. $this->Form in your controller/templates as usual.
  • Some other suggestions of ways to address the problem were made (although neither of these would have been easy to implement in our case in a reasonable amount of time):
    • saving unsubmitted data on the client side using local storage; and
    • having the filled values be added to the query string for the login redirect and using these to repopulate the form afterwards.

You’re still getting some benefit from the FormProtectorComponent? It’s optional, so you could just disable it entirely if it’s not adding anything for you.

@Zuluru The FormProtector still provides a number of benefits even if the hash does not include user-specific information. For example, it prevents the submission of additional fields, url tampering and hidden input modification, as well as submitting payloads to endpoints for which no forms are generated by the server.