Can frontend searches be altered?

Hello,

Let’s say :

  1. The data API is DISabled
  2. No privacy rule on Thing A (publicly accessible)
  3. On my page there is a “Do a search for : Thing A”

My question : Can the search be altered by a front-end genius, like removing or modifying parameters etc… ?

What I want : Thing A to be accessible only if you know it’s Slug or it’s ID → my frontend search is “Do a search for Thing A : where Slug is X”
What I do NOT want : some genius to change the querry and somehow gets access to the list of all objects of type Thing A”

The documentation remains quite obscure around how things are secured and run under the hood. What the doc says “The fact that the data is downloaded to the local device does not mean the user can freely tamper with it: that part is still securely handled on the server. It just means the data can be viewed.Client-side and server-side | Bubble Docs

EDIT : in the same way, can frontend workflow conditions be altered, or indirectly altered through data alteration?
Let’s say I have an “is_admin" boolean field on all my users, and conditions on my frontend workflow regarding this “is_admin” field. Can some genius alter it’s own user data, or the condition of the workflow so it can run the workflow?

My understanding is that if you have no privacy rules on a data type and it’s publicly accessible, it is technically completely exposed. Anyone who really knows what they are doing can find the data type name as soon as the page loads via browser dev tools, and then simulate a search call, and your Bubble app will return everything for that data type because it has no privacy rules.

But is it really ?

I believe frontend workflow somehow - for some part at least - are still run in the backend, when it comes to data and queries.

I remember reading somewhere a visual explanation of how a bubble webpage code is run, I believe there was some back and forth between frontend and backend in a way that some frontend code could not be spoofed or tempered with.
But I can’t find that information anymore.

I’d like to -up- my question and add a little something that bugs me as well with privacy rules.

It’s official that one can modify any field of a Thing he can “find in searches”, EVEN IF he is not allowed to view these fields.

Example : I might not be allowed to view “slugs” from the User datatype. BUT I can create a workflow that creates “slugs” for any user (where slug is not set, okok, well this is just an example).

I need to understand how frontend workflow are run, there is obviously a subtle something that makes it all safe and prevent a frontend user to make unwanted querries. But, what is it?

Front end workflows change things on the client device not in the database. All database writes are done server side even if ran from Front end. Basic sense is to never trust client device, so should verify server side before saving or using important details.

For example, if have stripe payments , do not trust values from device sent, like total amount or coupon value, instead, you should check server side if the values match, like is the total amount correct.

What is your specific data field and use case concern?

Hey boston85719, thanks for the answer

For a weekend project with no real challenge, I’ve created a “wish list” (christmas, birthday…) tool without the need to create an account. You create a list, put gift wishes in it, and you share the link with your loved ones so they know what to give you.
The idea is NO ACCOUNT, just a unique link for each list.
There are only 2 things, “List” and “Wish”. Each “list” contains a [list of Wishes], and that’s about it.

Even though there is no account, I wanted security thourhg obscurity → The user needs to already know the List’s slug or id, and make a direct-navigation to my page in order to be able to do anything (view / edit…)

And I told myself “easy", I will” :

  • setup privacy rules so that wishes are not searchable at all → so we can only find these through the [list of Wishes] inside the list object
  • only a few fields from Wishes are viewable so the user is able to see the bare minimum (name / gift link…) but not interact with the rest of it (name of the person who makes the fist / some other fields …)

I’ll have to figure something out later for limiting users to mass search for lists, but that’s for later.

After digging into bubble’s manual. I found that :

  1. one can indeed MAKE CHANGES to fields he is not allowed to SEE. Source : bubble manual
    View all fields does not stop you from Making changes to a thing and updating that field in a workflow. However, it can stop you from correctly checking a condition if the current user is unable to view the field on which the condition is based
  2. Bubble recommends to use Conditionnal Expressions on the frontend to ensure security. Source : bubble manual
    To protect workflows from performing tasks you don't want it to, you'll need to use conditional expressions in the Only when field on the workflow or specific action

→ this is weird how the “display” and “edit” rights are somehow melted together. Also, if the frontend is by definition unsecured, why would bubble recommend to use conditionnal expressions?

The reason Bubble recommends conditional expressions on frontend workflows is because privacy rules only control what data users can see or search for, but they dont stop workflows from running. So even if a user cant view a field, a frontend workflow can still modify it if no condition blocks it.

For your wish list app, your approach is actually pretty solid. Using the list slug or id as the access key gives you security through obscurity which works fine for low-risk data like gift wishes. Just make sure:

  1. The slug or id is long enough that it cant be guessed easily
  2. You have privacy rules so users cant do a search for all lists and find them that way
  3. Any sensitive fields like who made the fist are properly restricted

The display and edit rights being linked is just how Bubble works. Its a bit confusing but basically means you should always use conditional expressions on your workflows as an extra layer, especially for anything that writes to the database. Dont rely only on privacy rules for write protection.

Privacy rules are the only thing that restrict data access in Bubble.

That’s the only fact you need to know about data reads.

No. A condition like Current User’s is_admin is yes could not be modified by a user because Bubble checks this on their server after attempting to initiate the workflow. A condition like var - is user admin which references a state or group data variable could be manipulated.

Yes, really.

If autobinding is not permitted via your user’s privacy rules, then users can only create/update/delete things through a workflow context (well, the data API too). If those workflows are secure, then a user can’t just arbitrarily update a record like they can arbitrarily read data.

  1. Privacy rules are the only thing that restrict data access in Bubble.
  2. Autobinding allows fields to be modified arbitrarily if the user’s privacy rules allow them to
  3. Aside from autobinding, things can only be created, modified, or deleted through a workflow. Securing the workflow prevents that.
  4. Any client-side data like states or group data variables can for the most part be manipulated to be whatever the user wants it to be.
5 Likes

Hey @georgecollier so I shouldn’t base permission controls as group variables containing dynamic boolean returns (based on database lookups and other logic) to be used to allow or block actions, since this could be modified by the user on the frontend?

Would the same apply to reusable component properties?

What would be your suggestion for having centralized permission controls?

Correct.

Yes.

Better Boilerplate has this implemented in a maintainable and secure way.

Let’s look at the ManageAccount component (where users update name/profile picture etc) for an example.

The ‘Account’ we’re modifying is ManageAccount’s Account. Pretty straight forward; we want to re-use this UI for admins so we don’t just use Current User.

However, how do we know the user hasn’t just changed ManageAccount’s Account to any other arbitrary account that their privacy rules permit them to access?

To deal with it, we have a custom event that checks that this Account is one the user can modify OR they’re an admin. We end the workflow if they’re not authorised. In practice, this should never happen unless it’s a malicious attempt. The relevant workflow in the editor is here.

With that said, you obviously can’t use a custom event to control if, for example, something is visible (at least not elegantly). For this, duplicate the condition in the workflow and expose it as a reusable element property e.g Make this element (delete button) visible only when Global A’s Can modify account is yes.

We’re just using it to control visibility, and a technical user could bypass that anyway, so it doesn’t need to be secure. As long as the workflow associated with it is protected with a server side condition on the workflow, or some other server-side logic, it’s fine.

Of course, I did gloss over the option of ‘just put a condition on the workflow’. The reason I haven’t done that is it is a nightmare to maintain as you’ll have to duplicate that condition everywhere. The custom event functions as a reusable expression. One day Bubble might add reusable expressions / expression variables, in which case adding it directly on the workflow event would be more straight forward.

It’s a bit tricky to wrap your head around.

I shared a diagram internally to help explain the client/server-side wall and how Bubble does/doesn’t ‘trust’ values. I’ll write about it some time, but maybe it helps you. A workflow essentially loads all dependencies and will trust any client side dependencies.

P.S - I mean it when I say a technical user can force everything to be visible. Here’s the Bubble homepage with everything visible on page load :slight_smile:

3 Likes

All server actions will check the conditions on the server.

If those conditions are from the DB then the WF is secure - the server will check the DB values before deciding whether to run the WF.

So, in the case of an “is_admin" boolean field on your users, even if they change that on the front end (which is trivially simple to do) then the server will check the actual DB value when the action runs - so it is secure no matter what.

Obviously, client-side actions only use the client side data - so any client-side actions will run if the user manipulates the data on the front end - but that’s not a security issue.

That said, if the condition uses a client side value (such as a yes/no variable, based on the user’s Admin status) instead of using the actual DB value, then changing that on the front end (which is, again, trivially simple to do) will allow the server actions to run, as that’s the value they’ll use for the condition.

note: although the above is true for server actions, it is NOT true for data access - at least not when there are no privacy rules set.

If you have a search for things with a constraint based on some database value and you change value that on the front end, then that is the value that will be used in the search, and that’s the data that will be returned. Only privacy rules can prevent that.

This is correct - DB value themselves will be checked on the server, so are secure

But booleans based on the those database values are just booleans, and will be passed to the server as such if it’s those values used in the conditions - so can be easily manipulated on the front end, and so is not secure.

6 Likes

Building on @adamhholmes’s reply above, a helpful way to think about it is this:

  • Suppose you have a workflow A that runs Only when Current User’s isAdmin is yes
  • Suppose you have a workflow B that runs Only when var - isAdmin is yes. var - isAdmin is a state or group data variable that uses Current User’s isAdmin as a datasource (so is logically the same).

A is secure. This is because the value evaluated is Current User’s isAdmin - a database value.

B is not, because whilst var - isAdmin has Current User’s isAdmin as a data source, the value is just yes or no.

i.e, B uses a client side variable that is either a yes or no, whilst A uses a database expression that will necessarily be checked on the server.

2 Likes

Does bubble do something with field values of ‘is_admin’ or ‘isAdmin’ similar to language preference, so it kind of detects from user data type which field is the is admin Boolean field?

No, Bubble does not automatically detect or give special treatment to fields named is_admin or isAdmin. The field name is just a label for your reference.

Bubble treats all boolean fields the same regardless of what you name them. Whether you call it is_admin, hasAccess, or canEdit, Bubble will evaluate it the same way.

The key security point from this thread is: what matters is HOW you reference the field in your conditions, not what the field is named. Using Current User’s is_admin directly is secure because Bubble checks the actual database value on the server. But storing that value in a client-side variable and then checking the variable is not secure because the variable can be manipulated.

3 Likes

Thank you all for your various input, which helped me remembering a few things. (frontend WFs and backend are somehow tied together, but I keep fortgetting how exactly, thanks for the refresh)

Following the above, a few questions now if you may:

  1. Do “Workflow conditions” and “Action conditions” work the same as explain above? If the condition implies a server-side data, then the condition is evaluated server-side? So I can secure a single WF step, or the entire WF.
  2. If I am not mistaken, I believe this thread’s title’s question is not yet answered. If the front-end has a “do a search fo X with constraints Y”, can this querry be altered into “do a search for X with constraint Z”?
    1. If yes, then is it even possible to implement “security through obscurity” and make a thing reachable only when the user knows its IDs/slug?

Is this true even when the local variable is infact just a reference to a DB thing?

Example: let’s say there is a datatype called “rights”, and the frontend -solely for readability reasons- stores that object on a group called “my_rights” of type “rights” on the page, and then everywhere on the frontpage the conditions use “my_rights”

Yes, though of course it’s generally ‘easier’ to have one condition on the workflow rather than many on the actions.

Yes. The rule you need to know is that all users can access all data that their privacy rules permit them to access. What you have defined on your page / logic is irrelevant; a technical user can arbitrarily execute any search query on your database and see any data your privacy rules have granted them access to.

In theory, yes, by disabling Find this in searches in the relevant privacy rule. However, it’s pretty easy to accidentally expose a thing’s ID (e.g on a related data type) so wouldn’t really recommend it.

Thanks, pretty clear.

Ok, then if “Find in searches” is disabled means the frontend can not in any way access the data. So the only way to go is to use backend workflows & bypassing privacy rules, the backend workflow can then return the wanted data. Is this correct?

It can access it via the ID directly (e.g from a List of Things, or Thing X’s Y)

If you’re validating a parameter in a backend workflow, that’s server-side, right?

Example: You have a parameter “pages” in a backend workflow. It comes from a client side input. A condition that says “only if pages <5” will still be secure because the variable is being checked by the server.