Security reference

This page lays out best practices when it comes to making your app and your data secure.
Overview
The security of any application is crucial. And it must be approached with the utmost knowledge about how Bubble works, and about all the possibilities offered by this platform to protect the data of the app owner and its users.
No app should be considered complete if at least the following security recommendations have not been followed.
Make sure you have read, understood, and applied the following security guidelines in your app.

Understanding client vs server

Before diving into this guide, it's important that you understand the difference between:
  • 'Client': the frontend, the page, what the user sees in her/his computer.
  • And the 'server': the backend, where some actions take place: recurring events and database change trigger events.
You have to understand one thing: any data that's already in the client (the page, the user browser) is already exposed. Even though you can hide something on the frontend, or not show it, or not access it, an attacker could access it and/or change it.
Consequently:
  • Don't try to hide information by hiding an element in the page.
  • Don't download any data to the client that you don't want exposed (searches, variables, etc.).
A practical example of this is applying a condition to a workflow: Let's imagine that you want only users with the Role 'Boss' to make a change to a thing.
To do so, you apply a condition to the action to check that the user role is Boss, and you use the 'var - Current user role', a variable hosted in a group in the page:
The variable value is retrieved from the server but then it's in the page
This condition can be hacked
That condition can be hacked because is checked in the page, hence an attacker could change the value of the 'var - Current user role' as that info is already in the page.
If, instead, you use this condition:
This condition cannot be hacked because is checked in the Server. An attacker cannot change the value of the user role in Bubble servers.
Just remember: any important check has to be performed in the server, not in the page.

Privacy rules

If you were to apply only one section of this document to your app, it would be Privacy rules. They are the foundation of any Bubble app security and have to be carefully thought and set up from the moment you start planning the app and its database.

What are privacy rules

Privacy rules are a shield that prevents users from accessing (seeing) data that they shouldn't be able to access. Of course this would vary from app to app, but let's imagine some scenarios where you'd need privacy rules so:
  • A user shouldn't be able to see the chat messages of a conversation between other users.
  • A user shouldn't be able to see the payments or invoices of other users.
  • A user should not be able to see who a merchant's customers are.
  • A user shouldn't be able to see a shop transactions.
  • A user shouldn't be able to see the other user private notes or pictures.
  • Etc.

How do privacy rules work

Privacy rules are applied by Data type and prevent the data from leaving the server (see Understanding client vs server).
Imagine a user performing a 'Search for invoices' without adding any constraint. The server will then return all the invoices from all the users on your database and it'll send them to the user computer. Yes, you can add constraints to the Search when you are building the app, something like: 'Search for invoices whose Creator is Current user', that search will return only those invoices, but a hacker could make a Search for invoices without the constraint and have access to all the invoices on your database.
To prevent malicious actors from accessing data that shouldn't be able to access, we use Privacy rules.
Continuing with our invoices example, you'd have to create at least one Privacy rule so when the 'Current user is This invoice's Creator' this User would be able to access all the invoices that meet that restriction, and prevent any other user (Everyone else) to access them.
Once the two privacy rules are configured in your application, there is no way for a user to access invoices that he/she hasn't created. No matter if an unconstrained 'Search for invoices' is performed, the protected data would never leave the server.
You should probably have at least another rule for the App admin, and, if it's a marketplace, you'd have another one for the client. That will always depend on your app structure.

How to set up Privacy rules

Having robust privacy rules is essential in making sure that your server doesn't send data to a browser that the user is not supposed to see.
See link below for more information about setting those up.

How to use API Workflows to access data protected by privacy rules in a synchronous and safe way

We can use API workflows in two different ways:
  1. 1.
    Asynchronously: scheduling it from a workflow. The actions will run in the server but it wouldn't return any data except for a number (in text format) that it's the workflow id (we can use it to cancel it).
  2. 2.
    Synchronously: using the API Connector to trigger the API workflow. The actions will run in the server and it'll will return the data that we want in a synchronously way, so we can use that response in the following actions of the same workflow.
Let's imagine that you have to compare the code entered by a user with the one stored in the database on his/her User record (and protected by privacy rules).
In this case you cannot access the value directly with a Search, for example. Scheduling an API workflow won't return if the value is a match either. But you can pass the user and the entered code to an API workflow through an API Call. The workflow will then return if the code is a match with the one in the database.
The API workflow has to be accessible only by authenticated users, and it'll ignore privacy rules:
Then the workflow will return if the code is a match or not:
Then we have to set up the API Call in the API Connector. To do that we'll need first the Authorization key:
Our next step will be setting up the API Call:
Finally, we need to initialize the call:
Now it's ready to be used in a workflow and check if the code matches. Then, using the response from the API we can decide what to do next.
In this example we have accessed data protected by privacy rules in a safe way, making an authenticated API Call from the server and we've obtained an immediate response. For the authentication we've used an internal API token.

Testing privacy rules

To test whether privacy rules are set up correctly, it can be helpful to use a web browser's developer tools to examine what data is available to the user. Here's a page that shows how to do that with Chrome.

More resources

Your Bubble account

The security of the application starts with the security of your own Bubble account.
If the security of your account is compromised, an attacker could take control of the app.
Make sure that:
  1. 1.
    You have 2FA enabled in your account.
  2. 2.
    You are using a secure password.
  3. 3.
    Your password is stored securely in a password manager application, like Bitwarden (free for individuals) or 1Password (paid app).
  4. 4.
    You don't leave your Bubble session open in unattended computers.
Other users with app access also have to contemplate this rules.

Bubble editor Settings

Please review the following settings and apply them in your app editor Settings tab when applicable, thanks!

Editor access permissions

Keep it as Private app.

Username/password

While the application is on an Agency plan, both version-test and version-live are password protected. But when the app moves to a paid plan, password protection is not mandatory.
However, we must maintain the password for the version-test as a method of ensuring that future developments remain hidden from the public eye.
You can set up them in the Settings tab > General of your app.
Also, we must use a username and password other than 'username' and 'password', or anyone could easily access our unpublished app (or the test-version of the already published one), since that combination is the default one in all Bubble apps.

Set minimum password requirements

Use the Define a password policy functionality found in Settings > General section of the Bubble editor to set the minimum length, capital letter requirements, etc.
We recommend setting a minimum password length of 8 characters and requiring 1 capital letter and 1 number.
If you are using our free Canvas Bubble.io template, this is already taken care of.

Block all iFrames

If your app allows other websites to display it in an iFrame, then other developers can much more easily impersonate your website or use its content to show on their own websites. Impersonating your website is useful in the case of a phishing attack.
It is okay to allow all iFrames or iFrames from the same origin if your app has a valid reason for doing so and you are aware of the minor risks involved. Otherwise, it's best to block all iFrames of your site.

Enable SSL

SSL ensures that the data transmitted between the server and the browser is encrypted - make sure to set it up by checking the box located in the Settings > Domain/email section of the Bubble Editor.

API

Internal API Workflows

Any API workflow exposed as public is a security risk. To avoid it, if you add a workflow that it's going to be used only internally, uncheck the Expose as a public API workflow.

Public API Workflows

Any API workflow exposed publicly is an endpoint that anyone outside our app can reach. There will be two types of them, those that:
  1. 1.
    can be run without authentication: some workflows have to be accessible without authentication, some webhooks for example.
  2. 2.
    needs authentication: most of our API workflows will need some kind of authentication. Authentication can be made:
    1. 1.
      With a temporary token: your app users can authenticated themselves via API and receive a temporary token. Once authenticated, they can access workflows and data but following the privacy rules set up for them in the editor (unless the 'Ignore privacy rules when running the workflow' is checked).
    2. 2.
      With an API Token: be careful as users authenticated with an API Token (app editor > Settings tab > API > Generate new API Token) have administrative privileges and full access to the database (privacy rules don't apply here).
Be careful: users authenticated with an API Token have administrative privileges and full access to the database.
As said before, while there may be some API workflows that need to be exposed and executed without authentication, this will not be the usual. For most cases with public API workflows we have to:
  • Limit them so they cannot be run without authentication.
  • Do not ignore privacy rules.
Data API
If you have your Data API enabled, make sure that only the Data types you need are enabled. Also, there should be privacy rules always in place to protect those Data types for unwanted modifications via the Data API.
To access the data objects exposed by your Data API, there are 3 types of authentication levels, please pay attention to how and where the access permissions are defined according to each level:
  1. 1.
    Unauthenticated clients: permissions defined in the privacy rules for "Everyone Else" apply.
  2. 2.
    Authenticated users via temporary user token in the backend: permissions defined in the privacy rules for the User they are authenticated as apply.
  3. 3.
    Authenticated users via API token in settings: full unrestricted access to all data.
Each data type enabled in the Data API will offer new 3 options in its privacy rules: Modify, Delete and Create via API. They are disabled by default on existing privacy rules.
Also, as a rule of thumb, no unauthenticated user should be able to Create, Delete, or Modify objects via API.

Swagger

The Swagger Specification is a standard way to describe an API. It can be used to generate a documentation, or to let other tools integrate with the API. Bubble provides a Swagger Specification out-of-the-box when you activate the API for your application. To get it you can hit the endpoint:
https://appname.bubbleapps.io/api/1.1/meta/swagger.json or https://yourdomain.com/api/1.1/meta/swagger.json
You can read here more details about the Swagger Specification standard.
You should be conscious that your Swagger file gives all the information about your endpoints, and even if you hide it, the https://yourdomain.com/api/1.1/meta/ url also list your endpoints and reveal if you need to be authenticated to use them. So your endpoints should be always secured, as they are exposed for an attacker to try to access them.
Hidding the swagger it's not a security measure, it's just a little layer of obfuscation, as the meta url above contains all the same information only not in a swagger format. Don't let it give you a false sense of security.

Collaborators

Granting access to the app is something that cannot be done lightly. Adding collaborators to the app with full access could be a dangerous security hole for your app.
Be very careful who can access live data as there are multiple regulations protecting privacy, access, and data management.
Follow this rules to minimize the security risks that collaborators can pose to the app:
  1. 1.
    Only add new collaborators if they need to have access to the app.
  2. 2.
    Only add the permissions the user needs to do his/her job.
  3. 3.
    Remove users once they have finished their work on the app.
Permissions are granular: App access, Data access, Log access. And not every collaborator needs access to all of them. Assign them on a "needs to know" basis.

API Connector

The following sections are crucial to maintain our apps safe, please review them and always apply what's explaining here in your builds.

Protect your API keys/secrets/tokens etc

All the values you enter on the API Connector Authentication feature, shared headers, or shared parameters, are private by default.
Any values here are private and safe
But sometimes you build an API call that uses sensitive values that go in a specific API Call headers or parameters. In that case make sure that:
  1. 1.
    If the value is fixed: the value goes in the API connector marked as "Private".
  2. 2.
    If the value has to be dynamic, so you cannot mark it as "Private":
    1. 1.
      If the user cannot see the value: use the API Call in a backend workflow, as the dynamic value is going to be populated on the server (not in the page), the value is not going to be downloaded to the user's browser (therefore it won't be public).
    2. 2.
      If the user can see the value, because for example it's the user own API Key: you can make the API Call in a page workflow, but make sure you remove any test value from the API Connector (see next section).
    3. 3.
      If you are in doubt about if the user should or should not see the value: put the API Call in a backend workflow and no data is going to be visible to the user.
The token value is safe because is marked as private
Expose a parameter that should be private is a serious breach of an app security and it cannot be done under any circumstances.

Preventing sensitive data to be exposed

When a user navigate to our app, the browser download several files. Between those files there's one: app.json, that contains all the information the browser needs to render the page. And within that information, all the API Calls are also downloaded and, therefore, visible to the user.
It's advisable that you review at least one app.json in order to understand all the data the user has access to with little effort. To do so, please follow this steps:
  1. 1.
    Navigate to your app url, like https://YOURAPPID.bubbleapps.io/
  2. 2.
    In Chrome, open the Developers tools clicking F12
  3. 3.
    On the top bar, click on Console
  4. 4.
    Write "app" (without the quotes) and press enter
  5. 5.
    In the text that appears, click on the arrow and explore all the info that it's there.
    1. 1.
      You'll find your app API Calls in app -> settings -> client_safe -> apiconnector2
All your API Calls not private values are visible to any user
How to check our app public infomation
To prevent sensitive data to be exposed via your API Calls, read and apply the following guidelines:

Remove any test/initialization values

Any value that goes in the API Connector UI fields, and is not marked as private, is going to be exposed on your app app.json.
Remove all the real values (not private fields) from your API Calls.
All values have been replaced with placeholders

Clean the response schema

When you initialize an API Call, the API Connector makes a call and saves the API response so it can know how the response schema is. To see the response schema of an initialized app, you have to click on Manually enter API response.
The important lesson here is that that response schema is exposed on your app app.json. That means that if there's any sensitive data in it, it has to be removed before you deploy your application. If you don't remove it, that sensitive info is going to be deployed and visible to the user.
This API Call "Get token" response schema is exposing an access_token, and our app_id. It shouldn't.
To clean the response schema:
  1. 1.
    Click on "Manually enter API response".
  2. 2.
    Replace any sensitive data with placeholders, i.e.: replace an actual API key with the string "api_key", a user email with "user_email", and so on.
  3. 3.
    Click on "Save" to save the redacted response, make sure to keep all the correct data types as in the original response.
The redacted, and safe, response

Remove unused/test API Calls

Oftentimes, during development we have unused or test API Calls that are not really in use in the app.
The fact of them being test calls, not being used, or not being initialized sometimes can make us forget that they are there, and even makes us forget to clean them.
Those are dangerous as they are also added to your app app.json and downloaded to the user browser even though are not in use. And they can contain sensitive data, as we saw in the previous sections above.
So get used to remove them before deploying your application. And during the time that you need them, follow all the previously discussed precautions: use private fields, redact public values, redact response schemes, etc.

Clean your app

Usually developers create "test" pages to try some logic before implementing it in the actual page. And sometimes those are created to execute bulk updates to the database.
That kind of pages can pose a big security risk if users can access them and are able to modify data.
Before deploying your app, make sure you have deleted any "test" or deprecated page.

Don't store sensitive data in an unencrypted format

If your application needs to store very sensitive data, like Social Security numbers, credit card numbers, etc. in encrypted format. Better yet, find another company (like Stripe or Paypal) that can handle these very risky transactions, and integrate them into your app through Bubble plugins or using the API Connector.
Bubble does encrypt all data at rest, which makes data storage and data usage more safe if you have authentication and privacy rules properly set up. But if you've accidentally made your privacy rules more loose than they should be, someone may get a hold of your data. We recommend encrypting the values that go into those sensitive fields yourself and then decrypting them before they're used.
And, if you don't need to access the value of the secret data, like a password which you only need to compare if the entered value is the same as the stored one, but you don't need to know the actual value, is better to hash the data with salt. This is useful for passwords, internal custom Secrets or API keys, etc.

No "paywall" popups

In Bubble, you can create a popup that blocks the user from visually seeing or interacting with the page. However, these popups can be closed relatively easily using Developer Tools in the browser.
To put this in specific "Bubble-ese": you shouldn't rely on a popup with the setting This popup can't be closed by pressing Esc to lock users out of pages.

Redirection security

A common security measure is redirecting the user out of the page if he/she doesn't have the needed permissions to access it.
In Canvas we do this using the User field 'Role'. Then a workflow is triggered if the user enters or is in the page but his/her role is not in the list of allowed roles for that specific page.n
This kind of redirection, if done right, that's it: triggered inside a 'Page is loaded' and with a condition that can be check on the server (read client vs server), doesn't allow the user to access the page.
But if the user is able to access the page or if is already inside the page and then his/her role is changed, the data is already on the user computer. In that case, someone with enough technical knowledge could freeze a page before a redirect and access the contents of it, even could be able to trigger some workflows.
So the tools to ensure your app safety are:
  • Privacy rules (i.e., if they land on the page, they won’t see data they shouldn’t).
  • Adding a conditional (“if user is of this role”) to key sensitive workflows so they can’t be triggered by a hacker.
Note that secure redirects were introduced in Canvas in version 4.2. If you are working on an earlier version, the role will be checked using a page variable, which you already know is insecure. If so, you have to update the condition and place it in a 'Page is loaded' workflow (copy the settings shown in the screenshot in the User roles section).

User roles

Canvas uses a combination of two Option sets, 'Role' and 'Bubble Pages', and a workflow in the 'header' reusable to redirect users out of a page if they don't have the appropriate role.
You have to remember to update the 'Bubble page' Option set, adding new pages to it as you add them to the app, and setting up its attributes correctly:
  1. 1.
    Add new pages to the 'Bubble page' Option set.
  2. 2.
    Set their attributes correctly: Is private? and Roles.
  3. 3.
    Make sure that all pages have their Bubble page Option set correctly set up.
  4. 4.
    Add the action to check the role, and redirect the user if needed, to new pages added to the app that don't have the 'header' reusable.
The redirection workflow in Canvas with version 4.2 and higher
Be careful: If set up correctly (screenshot), the redirection prevents the user from loading any content in the page if he/she tries to enter and load the page, but if the user is already in the page, and then his/her role is changed, an enough tech savvy user could still be able to stop the redirection and access all the data already in the page.

Other security risks

Unprotected test pages

This is a serious and, unfortunately, frequent security vulnerability: leaving unprotected test pages.
Otfen times the developer build a page to test some logic or to do some bulk operations. Then, this supposedly temporal page is left in the app, unprotected and deployed to life.
All pages must be protected by role and better removed if they are not needed in the app.

Risks from plugins and custom code

Using community plugins and custom HTML in your Bubble app carries some inherent security risks with it.
One of those risks is that the plugin developer could be malicious and steal data from you, like by tracking all browsers which load the plugin on the front end.
Only use plugins from trusted developers.
And, if the plugin has been published with an Open Source license, its code is visible and can be reviewed to ensure it's secure to use.

XSS (cross site scripting)

Another risk is the possibility that plugins will allow raw user data to run javascript in other users' browsers.
Bubble's native elements should all have protections built in to prevent users from executing javascript when users' data is shown to other users. Plugins, however, may not all have this set up. (The Bubble HTML element, of course, is an exception here, as it is intended to be able to include HTML and javascript.)
This is a risk when showing data on the front-end to users - for example, if I have a calendar plugin and I use it to display data from many users to each other, it's possible that one user will enter some executable code in a text field and that the plugin will execute that code when trying to display it. There are many different ways to exploit XSS vulnerabilities, so the specific ways to test this will not be covered here.

Unsafe HTML in emails (phishing)

Emails can be used as a vector to steal user data, user credentials and other sensitive info. This relatively simple attack has been the cause of several high-profile cybersecurity breaches over the past few years.

More resources

Forum resources

If you're interested in learning more about how Bubble secures data in general, and some other security recommendations, you can read these security guidelines from early 2019 posted in the Bubble Forum by Josh (Bubble CTO and co-founder).

Ebook

Security tools

You can also learn more about Security tools:

Final note

Thank you for making it this far and studying the complete guide.
The security of your applications and data is something that all our customers take for granted, but that is something that does not come by default in Bubble applications, rather it requires concrete actions on our part when building them.
Apply the contents of this guide to build secure applications and reduce the risk of security holes or data breaches.
Thank you for building securely!