Velo: Security Best Practices

Visit the Velo by Wix website to onboard and continue learning.

In general, your site is secure without you having to do anything. Wix takes care of that for you. However, there are certain situations where you have to take some precautions so that you don't expose your sensitive data to your site's visitors.

Collection Permissions

You should always set the permissions of your database collections to be as restrictive as possible. Generally, permissions for a collection should be granted for the Admin role only, unless there is a reason to grant a specific permission for additional roles. Even when there is a reason to grant a permission for more roles, you should only grant it for the roles that need it.

Here are some examples:

ExampleRecommended permission settings
Scenario: Form submission
An input form that you want anyone to be able to use.
Solution
On the collection that the form is connected to, set the create permissions to the Anyone role. Keep all the other permissions restricted to the Admin role.Read: Admin
Create: Anyone
Update: Admin
Delete: Admin
Scenario: Site content
A page that displays content from a collection to anyone.
Solution
Set the read permission of that collection to the Anyone role. Keep all the other permissions restricted to the Admin role.Read: Anyone
Create: Admin
Update: Admin
Delete: Admin
Scenario: Member-generated content
A member-generated comments section where members can post comments that anyone can see, but only the poster can update or delete the comment.
**Solution
**Set each permission based on who needs to access it.Read: Anyone
Create: Site member
Update: Site member author
Delete: Site member author

The collection permissions model contains a number of presets that automatically set the permissions for common scenarios, such as the ones mentioned above.

Unused Permissions

Be careful when granting a permission to a collection even if you don't expose that collection’s functionality in your site.

Here are some examples:

  • **Unused create permission

    **If your site doesn’t contain a form for site visitors to create content for a specific collection, you may think you can safely set the create permission for that collection to Anyone. That is not the case. A malicious site visitor can inject data into your collection without a form. Make sure your collection is protected by restricting the create permission to the Admin role.

  • **Unused read permission

    **If you have a collection for internal use that you don't use on any of your site's pages, you may think you can safely set the read permission for that collection to Anyone. That is not the case. A malicious site visitor can still read the data from this collection. Make sure your collection is protected by restricting the read permission to the Admin role.

Temporarily Bypassing Collection Permissions

Sometimes, you may need to grant access to collection data only in a specific situation or only to a specific user. You may need to expose some of the data in a collection, while keeping the rest private. In these cases, extending the permissions of the collection to more users is a security risk. Doing this exposes all the collection data to users with the permitted roles all the time. Instead, set the collection permissions as appropriate for most situations. Then, when you need to grant access to the collection to a specific user or for a specific use case, perform that operation in backend code.

You can grant temporary access to collections using the suppressAuth parameter that’s available as an option for many Wix Data functions. Setting suppressAuth to true allows a data request in backend code to interact with a collection even if the site visitor requesting the data doesn’t have permission to access that collection. To grant temporary access to a collection, define a function in your backend code that makes a data request using suppressAuth. Export this function for use in your frontend code.

Note

  • A backend function that suppresses Wix Data's permissions checks must do at least one of the following:
    (a) Perform its own checks before accessing a collection.
    (b) Filter out sensitive collection data before passing anything back to frontend.
    Anyone can call exported backend code, as described below. This code therefore presents a serious security risk. 
  • Only suppress permission checks when absolutely necessary. Leave Wix Data’s permissions checks enabled as much as possible. This way, even backend operations run only for users who have been granted permission for that operation.

Here are 2 examples of using suppressAuth to grant specific and temporary access to collection data:

  • **Access for site members with specific roles

    **Wix allows you to create custom roles for site members. However, you currently can’t set collection permissions by role. To allow only members with a certain role to access a collection, set the collection’s permissions to Admin. Then, implement backend code that suppresses permission checks for site members with the desired role.

Show me how

This code defines two functions:

  • isPermitted() : Uses the Wix Members API to check if a site member has a Staff role
  • queryCollection() : Uses suppressAuth to return a collection's data if isPermitted() returns true.
Copy
1
import { currentMember } from 'wix-members-backend';
2
import wixData from 'wix-data';
3
4
async function isPermitted() {
5
try {
6
let hasPermittedRole = currentMember.getRoles()
7
.then((roles) => {
8
const staffRoleCheck = roles.filter(obj => obj.title === 'Staff');
9
10
if (staffRoleCheck.length > 0) {
11
return true;
12
} else {
13
return false;
14
}
15
})
16
17
return hasPermittedRole;
18
19
} catch (error) {
20
console.error(error);
21
22
return false;
23
}
24
}
Copy
1
export async function queryCollection() {
2
const accessGranted = await isPermitted();
3
4
if (accessGranted) {
5
try {
6
let collectionData = wixData.query('myCollection')
7
.find({ suppressAuth: true });
8
9
return collectionData;
10
} catch (error) {
11
console.log(error);
12
}
13
14
} else {
15
return "Access denied";
16
}
17
}
  • **Access to specific collection fields

    **Some collections contain both private and public data. In this case, you may want to expose the public data to site visitors while keeping the private data secure. To do this, set the collection’s permissions to Admin. Then, implement backend code that suppresses permission checks and returns only the public data.

Show me how

This code uses suppressAuth to query a collection and returns only the '_id' and 'comment' fields.

Copy
1
import wixData from 'wix-data';
2
3
export function getData(){
4
5
return wixData.query("myCollection")
6
.find({"suppressAuth": true})
7
.then((results) => {
8
if (results.totalCount > 0) {
9
const filteredResults = results.items.map( (item) => {
10
return {"\_id" : item.\_id, "comment" : item.comment}
11
})
12
13
return filteredResults
14
}
15
})
16
.catch((error) => {
17
console.log("Error:", error.message);
18
});
19
20
}

Storing Personal Information in a Collection

You can store a site visitor’s personal information in a collection, provided that you set the collection permissions so that only the Site member author can read, update, or delete content.

You may also want to store visitor information using the built-in Contact List. This allows you to use this information with all the other contact list functionality that Wix provides. You can then also use Velo APIs to perform operations that involve site members or contact data.

The following APIs are available:

Signup Forms

When you set up a member signup form for your site, you have 2 options for limiting who is allowed to sign up:

  • Everyone: When a new member signs up, they are approved automatically. You do not need to do anything.
  • People I approve: When a new member signs up, you receive a notification, both by email and in your site's dashboard, asking if you want to approve or reject them. Only those who you approve become site members.

If your site members have permissions for any of the collections on your site, then choosing Everyone is the same as setting those permissions to Anyone. This is because anyone can now become a site member and use the site member permissions. This is a potential security concern. You should think carefully about whether site members have access to any sensitive collection information before allowing anyone to become a member.

Code Visibility

All PagePublic, and masterpage.js code is visible to any site visitor. Even Page code on a password protected page or members only page can be viewed and manipulated by any site visitor, including those who don't have the password and are not members. Therefore, it’s extremely important that you don't expose any sensitive information in Page, Public, or masterpage.js code.

Backend code isn’t visible to site visitors. It's safe to use sensitive information there and export the results to frontend code. However, even though malicious visitors can't see what exported backend functions do, they can still call those functions if they know their names.  They can do this using any arguments they want, and examine any return values. Therefore, any exported backend code should perform validations before carrying out potentially harmful operations or returning sensitive information. Also, backend functions that are not called from public code should not be exported.

API Keys

If you access a 3rd party service that requires an API key or other sensitive information, you should always store that information in the Secrets Manager. Never use API keys in Page, Public, or masterpage.js code. Instead, export a function from a backend web module that uses the Secrets API to extract the key and calls the 3rd party service. Call that function from your Page, Public, or masterpage.js code.

Velo Signup and Login Forms

If your site has a Member's Area, you can add forms using Signup & Login pages. One type of form you can add is a Velo Form, which lets you use code to customize member signup and login. Because signing up and logging in involve the transfer of sensitive information, it’s best to prevent signup & login API calls from running in the frontend when using a Velo Form.

Validations and Checks

If you want to perform a security check or validation in your code, you should always do so using Backend code. Any check or validation in your PagePublic, or masterpage.js code can be easily read, manipulated, and circumvented by a malicious site visitor.

For example, consider the following Page code intended to reveal a secret key to a specific site visitor:

Copy
1
import {currentMember} from 'wix-members-frontend';
2
3
$w.onReady( () => {
4
$w('#validateButton').onClick( () => {
5
currentMember.getMember()
6
.then((member) => {
7
if(member.loginEmail === 'secretemail@mail.com') {
8
// show secret key
9
$w('#secretText').text = '43ne5gfou94tfe';
10
} else {
11
// show denial message
12
$w('#secretText').text = 'Access denied!';
13
}
14
})
15
})
16
});

There are two major problems with this code:

  1. The security check is revealed to all site visitors. Anyone can see that the correct email address is secretEmail@mail.com.
  2. Sensitive information is revealed to all site visitors. Anyone can see that the secret code is 43ne5gfou94tfe. 

The correct way to do this is to move both the security check and the secret code to a backend web module as follows:

Copy
1
// In backend file: secureModule.jsw
2
import { currentMember } from 'wix-members-backend';
3
import { getSecret } from 'wix-secrets-backend';
4
5
export async function secureCheck() {
6
try {
7
const member = await currentMember.getMember({fieldsets : ['FULL']});
8
const memberEmail = member.loginEmail;
9
const secretEmail = await getSecret("secretEmail"); // "secretemail@mail.com"
10
11
if(memberEmail === secretEmail) {
12
const secretValue = await getSecret("secretValue"); // "43ne5gfou94tfe"
13
return secretValue;
14
}
15
else {
16
return "Access denied!";
17
}
18
}
19
catch(err) {
20
console.error(err);
21
return "An error occured";
22
}
23
}

Although malicious site visitors can call the backend function, they can't gain any information by doing so since the function doesn’t reveal any sensitive information. You can call this function safely from Page code as follows:

Copy
1
import { secureCheck } from 'backend/secureModule';
2
3
$w.onReady(() => {
4
$w('#secretButton').onClick(() => {
5
secureCheck()
6
.then ((message) => {
7
$w('#secretText').text = message;
8
});
9
})
10
});

This page code is visible to any site visitor. However, since all the security check logic was moved to backend code, seeing the code doesn't reveal any sensitive information to malicious site visitors.

Note You can also set permissions that define who can call each individual exported function in a web module.

Was this helpful?
Yes
No