Understanding and Securing Public Backend Workflows in Bubble
The Common Misconception
After misconfigured privacy rules, public backend workflows represent the second most prevalent security issue in live Bubble applications based on what I’ve seen from auditing dozens of apps. There’s a widespread misunderstanding about their security model that you may not be aware of.
Many developers assume that requiring authentication is sufficient to secure a workflow. However, this assumption is incorrect. When a backend workflow requires authentication, it only verifies one of two conditions:
- The workflow is called using an admin API token
- The user is logged in
The second condition is where security vulnerabilities commonly arise. I’ll walk you through a couple of examples.
Example Vulnerabilities
Example 1: Unrestricted User Deletion
Consider a recruitment marketplace with a backend workflow named ‘delete-user’. This workflow accepts a User parameter and performs the deletion operation. It might be public either by Bubble’s default settings or because it’s called from the front-end using the API / App Connector.
The security issue: When authenticated, any user can call this workflow without restrictions. From Bubble’s perspective, authentication is satisfied. This means any user could potentially delete other users as long as the privacy rules allow access to those user records - which in a recruitment marketplace could be a substantial number of users.
Example 2: Email System Exploitation
Consider a ‘send-email’ workflow that accepts three parameters:
- recipientEmail
- subject
- body
Even with authentication required, this creates a significant vulnerability. Any authenticated user can:
- Send emails to any email address
- Include any content in the email
- Have the email sent from your platform’s email address
This scenario creates a perfect vector for phishing attacks through your platform’s email system.
Just because a user is authenticated, does not mean that they should be able to run the workflow. Authentication is generally necessary, but is rarely sufficient.
How to resolve the issues
The appropriate solution depends on your workflow’s specific use case. Here are the detailed approaches:
1. Internal-Only Workflows
If the workflow is exclusively used internally (e.g., via Schedule API workflow):
- Uncheck the ‘expose as public workflow’ checkbox
- Deploy the changes live
2. External-Only Workflows or API Connector
For workflows called only from external APIs:
- Add a text parameter (e.g.,
key
,token
, orauthKey
) - Configure the first workflow action as ‘Terminate this workflow’
- Add a condition: 'Only when key is not
<your chosen key>
.
This creates a password-protected workflow that terminates if the key is incorrect. - If calling the workflow from the API Connector, provide the auth key in a private parameter.
3. Workflows called by App Connector
Expand for details, as the solution is more complex.
For workflows used both internally and called from the front-end via App Connector (where parameters cannot be private).
Create a new ‘Permission Token’ data type with fields:
- token (text)
- User (User)
- expiry (date)
- used (yes/no)
Set the privacy rule: This Permission Token’s User is Current User
Before calling the backend workflow:
- Create a new Permission Token
- Set token = Calculate random string (use a long random string)
- Set User = Current User
- Set expiry = Current date/time + 1 day (don’t set this too short, else users with unsynchronized system clocks may not be able to authenticate)
Pass the Permission Token’s token to the backend workflow
Within the workflow, implement these checks:
- Search for Permission Tokens where:
- token matches the token parameter
- User matches the User parameter
- expiry is greater than Current date/time
- used is no
- Terminate the workflow if no matching token is found
- Mark the token as used if found, then proceed
This verification system ensures the backend workflow is called within your app’s workflow context rather than through direct API manipulation. You can implement it easily using a custom event that returns a verified value (yes/no), and terminating the workflow when verified is no.
Identifying workflows at risk
You can go through your workflows to identify which are public, and determine if they pose a threat.
However, I’ve built a new audit tool which is available in early access that offers comprehensive security scanning capabilities (including for this issue):
Features:
- Public data detection (bypassing data API)
- Backend workflow exploit identification
- Insecure and inefficient plugin detection
- Misconfigured redirect detection
- Automated issue fixing in the editor (one-click issue fixes)
The tool provides context-aware scanning - for example, it won’t flag public products in an e-commerce app unless they have specific sensitive fields.
Upcoming features:
- Role-based privacy rule analysis
- Internal app logic security vulnerability detection
- App logic optimization opportunities
- Interactive app analysis capabilities
While Flusk provides excellent ongoing monitoring, this new tool focuses on comprehensive point-in-time audits, for free. I hope that as Bubble begins to integrate security more directly into the editor, that this tool becomes less relevant, but I bet that I can build security tooling faster on Bubble than Bubble can built it into Bubble!
For early access to the free tool, visit: https://secure.notquiteunicorns.xyz