In preparation of our upcoming developer conference, we started a bi-weekly coding challenge on our Discord Server.
Code challenges can be a great way to learn about different APIs, services, and integrations available on the Velo platform.
Our first challenge focused on WeDu, a fictional project management SaaS business that created a site on Velo. If a customer signs up and subscribes to a Subscription Plan on their site, they will gain access to a page that would allow them to generate a product key for their software.
Unfortunately, as participants of this challenge discovered, the WeDu engineers wrote an insecure implementation.
Page Code
import { getProductApiAuth, updateContactProductKey, getProductKey} from 'backend/product.jsw';
import { fetch } from 'wix-fetch';
$w.onReady(async () => {
const productKey = await getProductKey();
if(productKey === undefined) {
$w('#keyInput').value = "Click 'Generate Key'"
} else {
$w('#keyInput').value = productKey;
$w('#generateKeyButton').disable();
}
const apiAuth = await getProductApiAuth();
$w('#generateKeyButton').onClick( async () => {
if(productKey === undefined) {
let request = await fetch("https://wixdev.net/_functions/product/generateProductKey", {
"method": "POST",
"body": JSON.stringify({ key: apiAuth })
})
let response = await request.json()
let productKey = response["productKey"];
$w('#keyInput').value = productKey;
$w('#generateKeyButton').disable();
await updateContactProductKey(productKey);
}
})
});
At first glance you may not see anything wrong with the code, but as you explore you'll see:
The API to generate a new key and its request structure is visible on the client side code. This means a malicious actor can call the API if they find the authorization key.
The API authorization key can be found by a malicious actor. Even though it is not in plain text, it can be extracted using Developer Tools in any popular web browser as it is saved on the client side using getProductApiAuth()
updateContactProductKey() trusts user input from the client side. A malicious actor can use this function to input data into wherever it is saved.
The Solution
Move API calls (especially authenticated APIs) over to the Backend JavaScript Web Modules (JSW)
Use Wix-Secrets-Backend API in your backend JSW files to prevent your API authorization keys from being leaked or visible in plain text
When writing backend functions make sure they expose as minimal data necessary to the client side (Page Code) to prevent bad actors from studying your site structure and using that knowledge for exploitation
Don't trust your user inputs by default! Make sure to write backend validation/sanitization logic to check from where or how a request is attempting to write data. For example, updateProductKey() should not work if it is called from any user other than the admin within the backend JSW files.
Solution Example
To take a look at an example of a solution with a secure implementation, check out the following submission (template available here) by @Kentin from Code Enhancement Studio
On top of all of the best security practices mentioned above, he was able to:
Write modular code which made the functions more reusable across the site
Clearly document the functions to improve the code readability
Note - To replicate the intended functionality you will need to take the following steps before publishing your site:
Add a Custom Field to Contacts titled "Product Key" of type "Text" (Learn More). This will be converted to “custom.product-key” in the backend under a custom contact field
Change Permissions for the "Generate Key" Member Page to only be viewable by Subscribers (Learn more)
What's next?
We just launched our Code Challenge #2! 😎
For this challenge we are asking participants to test the next iteration of WeDu's site!
We will accept entries between July 1 - July 11 (11:59 PM EST)
Submit a valid entry to earn Velocoin, which can be exchanged for virtual prizes!
To get started, check out the submission form for Code Challenge #2, or join our discord server