In this weeks FAC (Friday Afternoon Contemplation), we will see how your program might reveal data that it really shouldn’t.
Suppose you are asked to write a membership and partner finder for the imaginary LuvShack, a site where people can find others to indulge in carnal pleasures of a certain kind.
Your membership collection would like like this:
You have built a form where people can enter their information (like real Name, date Last Hiv-test and Hiv-result), but you make sure to secure the pages with a “Members only” lock.
The search result for partners might look like this (see https://girizano.wixsite.com/codecorner/dataviz)
(This is a simple page: no coding, just a dataset and a repeater hooked up to the dataset.)
As you can see, you took care not to display that sensitive data inside the repeater. But ……
Wix Data and Wix Dataset perform, by default, a “select all fields”
If you are familiar with SQL, both wix-data and wix-dataset do a
select * from tablename
instead of
select title, image from tablename
meaning that all columns of the Collection/table are selected and handed over to your frontend program. “Who cares?”, you might think, “I don’t display them anyway”. Well, you might not display them, but they are definitely visible.
Do the following:
1) press CTRL-I (Windows) in your browser
2) click the Network tab
3) load the above page ( https://girizano.wixsite.com/codecorner/dataviz) or, if you had already done so, reload it.
4) in the Network tab, click “All” or “XHR”
5) sort the first column (click the header) and look for an entry that starts with “find.ajax?”
6)click it. A new window will appear
7) select the “Response” tab (or “Preview”, but here we prefer the “Response” tab)
8) it will look something like this:
9) the response is long. Copy it entirely and paste it into NotePad or something similar
10) in NotePad, it will look something like this:
11) look closely at the returned object. Eventually you will see that “LastHivTest”, “Result” and “realName” are listed: you just revealed private information that you should not have!
This goes for everything
It was said before, but I’ll repeat it: this problem exist with both wix-dataset and wix-data, independent if you just connect a dataset to a repeater, “filter” it in code, call wix-data from your page code or call wix-data on the backend: if you do not do anything, ALL COLUMNS WILL BE HANDED OVER TO THE FRONTEND.
How to solve it?
There are 2 ways to solve it:
Wix-dataset without code (always frontend) : afterQuery hook
Wix-dataset with code (“filter”, always frontend) : afterQuery hook
Wix-data query from frontend : afterQuery hook
Wix-data query from backend ; afterQuery hook/code in function
If you haven’t done so already, read the documentation on the afterQuery hook ( https://www.wix.com/velo/reference/wix-data/hooks ). This hook is triggered in every situation where data has to be selected from the collection (so yes, also wix-dataset, although you cannot do a .query from a dataset).
The “clean” version
Now, from the top menu, click “Data Visible Demo2” or just load “https://girizano.wixsite.com/codecorner/dataviz2”.
You will now see this:
You will see that suddenly, every name carries the word “(patched”) after it (so you can see it did something) . Also, do the same as we did with the first example: get the “response” from the “find.ajax?”.
If you did this and copied the response object again to NotePad, it will look like this:
(Note: it runs on a copy of the first table, because the afterQuery is always defined on a Collection)
You will see that it holds no private data. How did we do this?
The afterQuery code from our example:
export function LuvShackMembersCopy_afterQuery(item, context) {
if (context.userRole === "anonymous") {
item.title = item.title + "(patched)";
item.lastHivTest = undefined;
item.result = undefined;
item.realName = undefined;
}
return item;
}
You see that we added the word “(patched)” to every title (so you can see that it actually does something) and we set the values for our 3 sensitive fields to “undefined”. That’s it. There are more ways to do a “projection” (the expensive, incomprehensible official term for this “field selecting”), but that is up to you (and read on).
A Caveat
There is (as always) one small big problem with using an afterQuery. Remember that above, we said “This hook is triggered in every situation where data has to be selected from the collection…”?
Well, if you implement this kind of afterquery in your code, just go to Content Manager. To your surprise, you will see this
The cause? Content Manager (CM) ALSO triggers the hook (and you can’t turn it off). And this is kind of confusing: is my data there or not? And what does it hold? Rest assured, your “real” data is still there, the hook just prevents it from showing inside CM.
How to solve that one?
Have a look again at the code. See the “if-statement”? Well, in the example we check there if the user is anonymous and if so, we do our projection. This will also show all data (=not performing the code) in CM (because Admin).
I know that is not a real world scenario, because on a site like this, all viewers will probably have to sign up. In that case, you will probably want to show the data only when the viewer is the dataOwner (to fill out the data on a form in the first place) or the Admin. Just look at the docs for users and roles. It is outside the scope of this FAC to dig into it further.
Wix-data and code in a function
So far, we have covered 1 out of 2 possibilities. There is one left: calling a backend function from the frontend using wix-data (solution 4 in above list). This is the safest and most flexible approach. It would work like this:
1) at the frontend (after a selection of criteria), you call a backend function with only the selection criteria as an argument, like:
getSelectionJSW(objCriteria);
2) this function getSelectionJSW resides in a JSW-file. In it, you add an array of fields to return, like(and not at the frontend, that might be manipulated):
let arrProjection = [“title”, “image”, “_id”, “_owner”, “_createDate”];
3) this function calls a function genQuery inside a .js file which accepts several parameters (collection name, query, limit, retries, … ) and the array
4) inside this generic Query Handler, you perform the query, the projection and return ONLY the fields requested to the JSW-function
5) the JSW-function returns it to the frontend
6) done
This way, you do not have to use afterQuery and all the exceptions for different kinds of Roles. But remember, a backend function called from the frontend HAS TO return a response within 14 seconds. If not, you will receive the infamous timeout (“Did you forget to resolve a Promise?”), but, the backend process will keep running (until it runs into another kind of timeout).
To get an idea, read this Wix-article: https://support.wix.com/en/article/velo-best-practices-for-improving-performance-in-wix-sites-with-data, the section “Download only the fields your need”.
I try to never use datasets, and only query on the backend and only return the needed fields, but what about dynamic pages? I would really have the option for the dataset to only return the ID of the item, and let us query (securely on backend) the item and return the needed fields as many fields are sensitive and should not be displayed.
I like the --> "Giri-FACs" 🏆
Master Giri doing what he does best