Problem/Motivation
The Webform Encrypt module could be enabled at any point after installing the Webform module. It means at some point, we would have both original and encrypted submissions, and one of them wouldn't be able to retrieve. Similar scenario applicable when the encryption profile and/or key used in the profile changes.
Scenarios
Scenario 1: No encrypt
1. Enable Webform module
2. Create a webform node (i.e. nid=1).
3. Submit an entry (i.e. sid=1).
4. Export ALL submissions of nid=1
Expected: Data of sid 1 is exported correctly.
Actual: PASS: works by default.
Scenario 2: With a new encrypt profile
6. Enable Webform Encrypt module
7. Add a key
8. Add an encryption profile and assign the key created in #7
9. Edit the webform node
10. Assign the profile created in step #7 to a field(i.e. "email address" field) in the form
11. Submit an entry (i.e. sid=2) on webform node created in step #2
12. Export ALL submissions of nid=1
Expected: Data of sid 1 & 2 are exported correctly.
Actual: SUCCESS: successfully downloaded sid=1 & 2. As the sid=1 data saved as original and sid=2's encrypted data saved as serialized value with profile details (e.g. a:2:{s:4:"data";s:210:"def5020037cc29bb09c7efc78614c5370d0c16a384231a9c31cbb7a825f87f48b7794d74d02112e2fa1cf24ffe2f79f2df4f7a01fbea92822932339061e48ff7d1381dcee235dd364f4aef6864e9faa5e428e59edf94b430ebfe9d46e9ebac60464b99e17847e722fa";s:15:"encrypt_profile";s:15:"webform_profile";}), we don't really loose any data in this scenario.
Scenario 3: With an updated key
13. Update the key created in #7
14. Submit an entry (i.e. sid=3) on webform node created in step #2
15. Export ALL submissions of nid=1
Expected: Data of sid 1 , 2 & 3 are exported correctly.
Actual: FAIL: successfully downloaded sid=1 and 3, but sid=2 data currepted/empty.
Scenario 4: With an updated encrypt profile
13. Edit the encryption profile created in #8 and update e.g. "Encryption method".
14. Submit an entry (i.e. sid=4) on webform node created in step #2
15. Export ALL submissions of nid=1
Expected: Data of sid 1, 2, 3 & 4 are exported correctly.
Actual: FAIL: successfully downloaded sid=1 & 4, but sid=2 & 3 data currepted/empty.
Scenario 5: without an encrypt profile
16. Delete the encrypt profile created in step #8
17. Submit an entry (i.e. sid=4) on webform node created in step #2
18. Export ALL submissions of nid=1
Expected: Data of sid 1, 2, 3, 4 & 5 are exported correctly.
Actual: FAIL: successfully downloaded sid=1 & 5, but data of sid=2, 3 & 4 are currepted.
Test setup
Above scenarios are tested with:
Encryption method: Real AES
Encryption key: Config based key.
Proposed resolution
Scenario 1: No encrypt - Already working!
Scenario 2: With a new encrypt profile - functionally working (Thanks to #2931711: Tracking an elements encryption status) but the old submission with original data in DB wouldn't be encrypted when assigning the new profile.
Proposed solution(s): As part of profile save, trigger the bulk encrypt to convert existing entries.
Scenario 3: With an updated key - Not working
IMO, this is the hardest of all scenarios as there are few cases the key can be changed/removed:
1. Update key from an encryption profile (via. admin/config/system/encryption/profiles/manage/[profile_name]
2. Update the key value in the existing key (via. admin/config/system/keys/manage/[key_name]
3. Change at source (e.g. update the content of key file directly)
Even though use cases #1 and #2 warn the user editing them would cause data loss, doesn't prevent the user to do so. As @Rob Holmes mentioned in #12 there could be a valid reasons (e.g. key breached) to update the key.
Proposed solution(s):
1. For use case #1, Disable option to change/delete encryption method/key at the profile - Suggest an alternative way to change them by creating a new profile with new key and method and switch profile. - This would align with the approach in the latest patch.
2. For use case #2, Provide an alternative approach for a key change - Create a new profile with a new key and switch profile - This would align with the approach in the latest patch. I also think some of these approaches should be pushed to upstream (encrypt and key modules) rather than try to solve here.
3. use case #3 is the tricky one and a) we could make it an exception and not provide any solution. b) Wonder if we could do a hash approach (like the core's config _core.hash) where we can keep the important data that could break the encryption/decryption as hash and try to check if any part of the profile has changed. The solution could be similar to #2931711: Tracking an elements encryption status where save the MD5 of profile data in a table. This could cause performance issues though.
Scenario 4: With an updated encrypt profile
Proposed solution(s): Solution in scenario 3 use case #1 applicable here.
Scenario 5: without an encrypt profile
Proposed solution(s): This would require bulk decryption similar to scenario 2, but it's mandatory to run it as part of field config update.
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Original report
:
I am marking this to review tomorrow as it just occured to me and havent had a chance to test.. but..
Pre Existing Data.
If a webform has taken pre-existing submissions before encryption is turned on for a field then all instances of that field in pre-existing submissions will need to be encrypted otherwise data will become corrupted on viewing/editing and then resaving.... possibly .. to be investigated.
Changes to encryption Profile
What happens if the encryption key or method changes whilst there is existing data etc. Do we need to decrypt all existing data and then re-encrypt using the new data.
If we are passing the encryption profile in from config when encrypting and decrypting then we always would need to keep all the data in sync with the currently selected encryption profile if it applies to all instances of that field.
Like i said I haven't yet tested out what happens in these scenarios but on the face of it there is a potential for data loss if they are not handled correctly.
Does anyone have any thoughts on this?
| Comment | File | Size | Author |
|---|---|---|---|
| #53 | 2925621-diff-52-53.txt | 583 bytes | vijaycs85 |
| #53 | 2925621-53-test-only.patch | 10.51 KB | vijaycs85 |
| #53 | 2925621-53.patch | 105.26 KB | vijaycs85 |
Comments
Comment #2
manuel garcia commentedI think we should definetly have a look at this @Rob Holmes, great point.
Let's first try to verify this, could be another critical.
Comment #3
rob holmes commentedI can confirm this is definately an issue to be dealt with. The problem to be solved as i see it is the fact there is no way to determine if a field has been encrypted or not at the point a new encryption profile is introduced to a field because as soon as you turn on encryption the storage controller will attempt to start decrypting the fields with no knowledge if it was previously encrypted or not.
I vaguely remeber the D7 field_encrypt module would store the encrypted data along in a serialised array which could then be used to verify if a field was encrypted or not by the presence of a serialised array with a specific key e.g.
['encrypted' => 'string that has been encrypted']Actually i think it also stored the encryption profile id used to encrypt it as it allowed you to change encrpytion types whilst still being able to decrypt old data as it had knowledge of how each item of data was originally encrypted.
We should also maybe take into account that such a change will invalidate any data encrypted using the current method, there are 31 sites reporting usage, i know that 15 of those sites are most likely to be from a large multisite i am currently working on.
I think the first port of call is to maybe write a failing test to verify the issue, then see if this can be accomplished without any major changes to how the data is stored.
Comment #4
manuel garcia commentedThanks @Rob Holmes for the analysis here, I think that your conclusion is spot on. We need to be able to find out for each submission if it has been encrypted and with what profile, and we should provide an update hook to migrate existing submissions as well, or else they might get corrupted. Would you mind updating the issue summary with this information, and include the proposed solution?
Bumping priority to critical as this could result in lost data / data inconsistencies / etc.
Comment #5
manuel garcia commentedI've been thinking about this, and I believe that the safest way to deal with this is to not allow enabling encryption on elements that already have data in them.
This approach would be by design so as to protect the integrity of the existing data. I honestly think this is the best solution here, as any other option would mean having the module bulk encrypt existing data, which would not only take a lot of time and testing to implement, but could easily run into unexpected errors, resulting in data loss.
If we can get consensus on this, I'll be happy to prepare a patch when I get some time.
Comment #6
manuel garcia commentedComment #8
manuel garcia commentedHere is a better approach at this than #6. Instead of disabling the form elements, show just the values and the message.
Comment #9
manuel garcia commentedThere would be no harm possible when adding a new webform element to an existing webform that has submissions in it, so allowing to configure webform_encrypt in this case would make sense.
Updating #8 accordingly.
Comment #10
gambryI left a comment yesterday, but for some reason I don't see it here :(
Anyway I completely agree with Manuel on #5.
I will have a look at the patch ASAP (still too early in the morning to switch on the brain :p), but I like the approach described.
Comment #11
gambryI like the approach. I'm going to manually test soon, in the meanwhile.
We could use
$encrypt_value = isset($config[$element_name]['encrypt']) && $config[$element_name]['encrypt'];here.Not sure why we use $element_name to check if this is a new element? Is this the best way?
I don't really like using markup in code, but that's my personal feeling. Would using the "list" theme be too difficult?
Comment #12
rob holmes commentedI've been thinking about this further and If we go forward with disallowing users to enable encryption for components with pre-existing data then I am unsure how we could deal with the following situation.
The only way to deal with this would be to uninstall the webform_encrypt module thus un-encrypting all the data and leaving it an original state.
The problem now is that you cant turn encryption back on using a new key/profile because there is existing data in the system meaning your only choice is delete all submissions before re-enabling encryption which is likely to be a bitter pill to swallow for a client who relies on the UI to manage historical webform submissions and the data contained within them when now they are now not available.
Comment #13
manuel garcia commented@gambry thanks for the patch review!
@Rob Holmes yeah you're correct, that would be a problem, here my thoughts on the subject:
Disabling encryption on an element then would HAVE to decrypt all entries for that element on the database, as that would be the expected behaviour. I think we could write some tools for dealing with encrypted webform submissions perhaps, but not within the build webform workflow. Perhaps add a link saying "If you want to disable/reconfigure this element's encryption, use this tool."
That's my opinion from a workflow point of view, that said, there are things that would need to happen if we we're ever going to get to that point: Right now is that we do not keep track of whether a submission element is encrypted or not. All we know is that a webform element is configured to be encrypted. Once that's the case, we encrypt new submissions to that element, and assume they are encrypted when viewing them.
In my opinion we should change how we save the data to the encrypted webform elements so that we can check whether or not a value is encrypted, and then we can start building these batch operations around reconfiguring and disabling webform elements' encryption. We should probably create a new issue and get that done before we release the beta. We discussed this on some other issue, cant find it though.
So in any case, I think for now the approach in the latest patch at least prevents data loss, and its the best I can think of doing with what we have...
Comment #14
thecraighammond commentedOpened a new task to track the current issue of knowing whether or not an element is currently encrypted.
#2931711: Tracking an elements encryption status
Comment #15
manuel garcia commentedThanks @thecraighammond for that. Looks like we're making good progress on #2931711: Tracking an elements encryption status so postponing this until we get that done.
Comment #16
rob holmes commentedBased on #13 i agree that during the build workflow is probably not the place to bulk encrypt and decrypt large amounts of existing webform subsmissions.
Attached is a proposed UI change to allow such operations to be managed on a dedicated screen per component.
I have added an indicator to the element name to show that is is encrypted and added a new option the the operations dropdown for managing encryption for that element. The functionality on the build workflow could stay exactly as it is now i.e. not allowing disabling or switching for components with existing submissions but can point to the more dedicated ui where those functions could be performed.
Comment #17
thecraighammond commentedAttached quite a big patch which covers managing (changing, disabling) of elements with encryption profiles as well as ui change @rob-holmes mentioned above. This is patched off the 8.x-1.x branch so includes any uncommitted patches up to this point that were required for this to work (and so might need to be broken up a little or patched from another point if it seems like a good approach).
Comment #18
thecraighammond commentedTrying out a new patch with the same changes - this one passes tests locally. Previous also failed locally (but with a different issue, strangely).
Comment #19
thecraighammond commentedPatch includes both serialisation changes, ability to change or disable profiles and the ui changes mentioned above.
I've added a work in progress patch to #2931711: Tracking an elements encryption status that should just be the serialisation.
Comment #20
thecraighammond commentedComment #21
shamrockonovHi guys, had a quick review of the patch in #2925621-19: Encryption of pre-existing data.
Unable to test the main scope of the issue due to two main issues in the patch at the moment.
Also, having a read I can see that a decision has been made on allowing a user to change encryption i.e re-encrypting the data, but what of providing an option to encrypt pre-existing non-encrypted data? Has a consensus been reached on this?
There are checks in place when displaying the data to determine if it data needs to be decrypted or not already, so if we did want to go down the route of encrypting existing non-encrypted data, what about providing an additional "encrypt existing data" tick box in the element_encrypt fieldset to be present if the field has existing data?
Edit: An additional issue I also came across was that webform_ui is a dependency as an error was thrown when attempting to go to the build page with webform_encrypt enabled.
Comment #22
shamrockonovUpdated the patch to contain the form controllers and added the extra dependency.
Comment #24
gambryThis issue was postponed in favour of #2931711: Tracking an elements encryption status, which is closer to a solution but not yet. In any case is not finished so this should be postponed until the other one is closed.
I don't think there is consensus on any of them. They both have downsides (re-encrypting data will corrupt data with Asymmetric encryption, with pre-existing data there is no way to know if data is already encrypted and with which profile and #2931711: Tracking an elements encryption status will be a starting point to be able to).
I guess the issue summary needs to be update to reflect what this issue is about and the split in favour of #2931711: Tracking an elements encryption status. It should also mention the thoughts on #13, and the suggestions on #16.
Comment #25
manuel garcia commentedWe can use
EncryptionProfile::validate()to find out if a value can be decrypted or not, in practice a check to see whether a value is already encrypted. This is whatEncryptService::decrypt()andEncryptService::encrypt()checks prior to doing their thing, so we could leverage that.Comment #26
gambryAs mentioned in #2931711: Tracking an elements encryption status #41:
This is my understanding by reading the code.
However a potential solution can be to try to decrypt the value, and:
I'm assuming 99% of the cases will be covered by first scenario.
Thoughts? Too crazy?
Comment #27
manuel garcia commentedYou are spot on as usual @gambry, thanks for doing this. At the moment I don't see a reliable way forward :(
Comment #28
manuel garcia commented#2931711: Tracking an elements encryption status is in, so we can proceed with this now.
Comment #29
gambryI submitted few weeks ago #2943231: Support for Public-key cryptography, trying to gather feedbacks and thoughts about supporting public-key encryption. To be honest with few small changes we can improve massively the security. And if manage to get in #2943231: Support for Public-key cryptography then this issue is easily solve-able by
trying decrypt() andcatching the 'can't decrypt' exception.Comment #30
manuel garcia commentedLet's postpone on #2943231: Support for Public-key cryptography which is RTBC.
Comment #31
jwmorris commentedWrong post - please ignore.
Correct post: https://www.drupal.org/project/webform_encrypt/issues/2990881#comment-12...
Comment #32
rob holmes commentedComment #33
manuel garcia commented#2943231: Support for Public-key cryptography just landed \o/
Comment #34
rob holmes commentedOK picking this back up :)
Comment #35
manuel garcia commentedOK just getting back to this after some time, we really need to do this :)
I'm finding it hard to figure out even what patch to continue with :s
@gambry @Rob Holmes can we summarize a bit the next steps?
So that anyone that wants to move this forward can do so :)
Comment #36
manuel garcia commentedIf we're going to depend on the work done as part of #2943231: Support for Public-key cryptography we will need to require encrypt 8.x-3.0-rc2 or later in our composer.json file.
Comment #37
rob holmes commentedI’ve actually just secured 5 days of company sponsorship to get this moving starting on Monday so i should hopefully be able to make some serious progress here.
If that changes for any reason I’ll at the very least cleanup patches and issues with the current state of things and unassign myself
Thanks
Comment #38
manuel garcia commentedGreat news @Rob Holmes - I'll try and help with this as well next week
Comment #39
rob holmes commentedJust so you know i am officially working on this all week :) Cleanup path to follow.
Comment #40
rob holmes commentedUploading my work in progress as i am a little stuck right now. (no interdiff as its a pretty much a completely different patch from previous)
Basically i cant trigger the batch process after a form submit due to the Ajax form, if i disable ajax then it kicks off fine. Was kind of hoping the form api would cope with a batch_set in a submit handler like it does nomrally but also when submitting via ajax as the webform build screen does.
Hoping Manuel Garcia has any ideas, otherwise might have to take it in a different direction.
Lets also see what test bot says.
I've brought over the bulk encryption class from the other issue as it is needed here (the other can probably be closed, but ill leave that until i get a bit further here).
Comment #41
gambryI haven't looked at it thoroughly - I will when tests bot is green - but just as first glance:
We need to early return in here, skipping this entry, as a possible scenario could be:
- data is encrypted
- profile can't decrypt (or any other error occur)
- $value at this point is still encrypted.
We don't want to double encrypt any entry, for many reasons.
We may need to early return on
decryptElement()catch block as well, even though that block is the last running for that method. This is to make sure in the future improvements of the code don't miss this piece of information and create troubles.Comment #42
vijaycs85Updated issue summary with different scenarios. I have tested them (except scenario 4) locally without the patch in this issue. Running the same scenarios with the patch would give some clarity on what this issue would cover. Feel free to add, if I missed any scenarios.
Update: Patch in #40 is still work-in-progress and not hooked with the system yet. so I will continue the conversation of what should we do to cover the scenarios in the issue summary.
Comment #43
vijaycs85Comment #44
vijaycs85Comment #45
vijaycs85Comment #46
vijaycs85Comment #47
vijaycs85Comment #48
vijaycs85Scenario 1: No encrypt - Already working!
Scenario 2: With a new encrypt profile - functionally working (Thanks to #2931711: Tracking an elements encryption status) but the old submission with original data in DB wouldn't be encrypted when assigning the new profile.
Proposed solution(s): Since its functional, I am not sure if it is really mandatory to update original data as part of assigning a profile. However, a drush command would be nice. Say, "update my data to current settings" then the command checks for all elements with profile assigned and make sure to convert the original data.
Scenario 3: With an updated key - Not working
IMO, this is the hardest of all scenarios as there are few cases the key can be changed/removed:
1. Update key from an encryption profile (via. admin/config/system/encryption/profiles/manage/[profile_name]
2. Update the key value in the existing key (via. admin/config/system/keys/manage/[key_name]
3. Change at source (e.g. update the content of key file directly)
Even though use cases #1 and #2 warn the user editing them would cause data loss, doesn't prevent the user to do so. As @Rob Holmes mentioned in #12 there could be a valid reasons (e.g. key breached) to update the key.
Proposed solution(s):
1. For use case #1, Disable option to change encryption method/key at the profile - Suggest an alternative way to change them by creating a new profile with new key and method and switch profile. - This would align with the approach in the latest patch.
2. For use case #2, Provide an alternative approach for a key change - Create a new profile with a new key and switch profile - This would align with the approach in the latest patch. I also think some of these approaches should be pushed to upstream (encrypt and key modules) rather than try to solve here.
3. use case #3 is the tricky one and a) we could make it an exception and not provide any solution. b) Wonder if we could do a hash approach (like the core's config
_core.hash) where we can keep the important data that could break the encryption/decryption as hash and try to check if any part of the profile has changed. The solution could be similar to #2931711: Tracking an elements encryption status where save the MD5 of profile data in a table. This could cause performance issues though.Scenario 4: With an updated encrypt profile
Proposed solution(s): Solution in scenario 3 use case #1 applicable here.
Scenario 5: without an encrypt profile
Proposed solution(s): This would require bulk decryption similar to scenario 2, but it's mandatory to run it as part of field config update.
I will leave this as a comment here and once reviewed by @Manuel Garcia, @Rob Holmes and @gambry, we could move this to issue summary "proposed solution" section.
Comment #49
manuel garcia commentedThanks all for the work so far on this, and the detailed scenarios and testing @vijaycs85 that really helps.
Regarding the proposed scenarios, I believe we should include editing submissions that were existing prior to enabling encryption on the webform. If my memory doesn't betray me I believe some of this may be partially covered by existing tests, but I've not checked if it covers this scenario. They should:
Regarding scenario 4 where we delete the encryption profile used by
webform_encrypt, I don't think we can reliably provide support for this? two potential avenues here:A similar situation for scenarios 3 and 5. We should at least display a message warning the user when they try to edit / delete them.
The scenarios 3 4 and 5 I believe could be out of scope here, although valid and actually very worth of follow ups since they fail. Opened about this though so up to you guys.
In any case, given the complexity/time consuming nature of manually testing these scenarios, and the criticality of this due to potential data loss, I believe we should consider going TDD here, nail down the tests first so we can more confidently progress forward. It's great we have scenarios almost ready since they describe the tests themselves already.
Comment #50
manuel garcia commentedIn my opinion the expected behaviour would be to encrypt existing data when you configure encryption for a webform. Similarly, users probably expect existing data to be decrypted when doing the opposite.
Comment #51
vijaycs85Thanks, @Manuel Garcia. Really appricate your time over the weekend to go through all points and providing valuable feedback.
#49:
I believe it is covered (or indirectly implies) in some of the scenarios depending on when the edit is happening. But definitely, something to have tests.
pretty much. so the scenario #3 use case #1 is:
Ok, I am +1 either way(here or follow up). I just want to make sure we know what is there, what is going to be the scope of this issue and what is not.
Totally. This is my next step; providing a test-only patch to all scenarios. If we end up splitting them as subtask(s), we can move the corresponding tests.
#50: got it, updated the solution accordingly.
Since I wasn't *completely* wrong with the scenarios and mostly @Manuel Garcia and I are on the same page, I updated the "proposed solution" section of IS with #48. @Rob Holmes and @gambry feel free to review/update.
Comment #52
vijaycs85Uploading tests for scenario 1 and 2. Setting to needs review' to test the patch. Locally I see one failure (trying to load SID=1 after SID=2) though:
Comment #53
vijaycs85weird. didn't throw that error locally until I ran a different test.
Comment #56
manuel garcia commentedIs #2990881: Create a Bulk Encryption/Decryption Service Class to facilitate encryption of pre-existing data duplicating efforts? can we join forces of both tickets?
Comment #57
rob holmes commentedComment #58
rob holmes commentedClosing this ticket in favor of https://www.drupal.org/project/webform_encrypt/issues/2990881