CRM_Core_Key - Provide more debugging hints about `qfKey`s
authorTim Otten <totten@civicrm.org>
Tue, 8 Dec 2020 23:15:45 +0000 (15:15 -0800)
committerTim Otten <totten@civicrm.org>
Fri, 11 Dec 2020 22:08:35 +0000 (14:08 -0800)
commit40b1007aabcfb23db0368f5c7471c5bd8559556b
treeccf52091aae4e76f4828b6401602584e5ffb2052
parentafba51fc6fe1833fd61faabe67533f1713f4c1c5
CRM_Core_Key - Provide more debugging hints about `qfKey`s

Overview
--------

The `qfKey` parameter is a security mechanism (CSRF). The content of `qfKey` is also inscrutable - so when there's a problem
with `qfKey`, it can be difficult to determine the origin/nature of the problem.

Before
------

The `qfKey` provides a digital signature based on (1) session ID, (2) the
form being processed, (3) a random private-key (unique to the user/session).
In some cases, the `qfKey` also has a random nonce appended to distinguish
between concurrent tabs that work with the same form.

* Ex: `2abc4b7f23d9ae4dfcdbb0c692cc666e5f11256fe84ace7662b0e075834b81958` (w/o nonce)
* Ex: `2abc4b7f23d9ae4dfcdbb0c692cc666e5f11256fe84ace7662b0e075834b81958_9527` (w/nonce)

If there is a logic problem where the `qfKey` of form A gets mixed-up with
form B, then it's extremely hard to understand the mismatch All you see are
two long random codes.

* Inputted qfKey: `2abc4b7f23d9ae4dfcdbb0c692cc666e5f11256fe84ace7662b0e075834b81958`
* Expected fqKey: `89874b7a23192e4d6aab10c622ca369e5e11226f784a5c7652b95075536b81a5e`

After
-----

The `qfKey` has a prefix to indicate the token's intended usage (as well as a digital signature).

* Ex: `CRMContactControllerSearch154eitbf74v3ko0sw94k08gogo8448asdfw4goggkkwgkww08c` (w/o nonce)
* Ex: `CRMContactControllerSearch154eitbf74v3ko0sw94k08gogo8448asdfw4goggkkwgkww08c_9355` (w/ nonce)

If there is a logic problem where the `qfKey` of form A gets mixed-up with
form B, then the prefix can help you understand. Compare:

* Inputted qfKey: `CRMContactControllerSearch154eitbf74v3ko0sw94k08gogo8448asdfw4goggkkwgkww08c`
* Expected qfKey: `CRMContributeFormContribution5o2due205mp0384080sgc8omw8kwggoksw47sswchs80gw0kgs`

This tells you that there's logical mismatch - it's trying to render the contribution
screen using the key for the contact-search screen.

Comments
--------

* The identity of the `formname` is not sensitive information.
* The `qfKey` might look prettier with more delimeters (`{formname}_{signature}_{nonce}`). However, I belive there are random bits of existing code
  which use `explode('_')` to split apart the signature and the nonce. This formula seems to be drop-in/interoperable.
* I switched the encoding of the signature from hex (0-9a-f) to base-36 (0-9a-z) to make it a bit shorter.
CRM/Core/Key.php