Before You Ship That App, Read This

Let me tell you about a situation I see all the time.

A tuition centre owner asks a developer to build a simple parent portal. Parents key in their IC number, the system shows their child's exam results. Nothing fancy. The developer says yes, builds it over a weekend, launches it. Owner is happy.

What nobody realised is that the data, every parent's IC number, every child's name, every phone number, is sitting completely open on the internet. No password needed. No hacking required. Just someone who knows where to look.

That is the story. Now let me walk you through exactly how it happens, and what to do instead. I will cover each platform separately so you can go straight to whichever one you are using.

What this article covers
  • Google Sheets and Apps Script: the Publish to Web trap, how to fix it, and what is safe to put on GitHub
  • Firebase: why your API key is not the problem and what actually is
  • Supabase: why your database might be open right now without you knowing
  • The one thing all three platforms have in common
  • Data masking: what to show and what to hide from users
  • What Malaysian law says about the apps you build

If You Are Using Google Sheets

You build a form, connect it to Google Sheets, use Publish to Web because it is the fastest way to get data in. You test it, it works, you move on.

The problem: Publish to Web makes your entire sheet accessible to anyone on the internet. Not just readable. Downloadable. All of it. A curious person presses F12 in their browser, looks at the network requests, finds the sheet URL, and walks away with every row of data your users ever submitted. They did not need to log in. They did not need special skills. They just needed to look.

The fix is to use Apps Script as a middleman. Think of it like a receptionist at a front desk. Your form sends a request to the receptionist. The receptionist checks who is asking, decides what they are allowed to see, then goes to the back room to get it. The back room, your Google Sheet, never talks to visitors directly. Only the receptionist does.

One Apps Script can handle two jobs at once. Receiving data that users submit, which developers call doPost, and sending data back when users request it, which is called doGet. You do not need two separate systems. One script handles both directions.

For login and access control, there are two sensible approaches depending on what you are building. For a staff login system, give each person a secret token that only they know. Apps Script checks the token before doing anything. For a parent portal where parents check their child's results, use the IC number as the search key. The parent enters their IC, Apps Script looks for that IC in the sheet, and returns only that child's data. Nothing else comes out. Other families' records never leave the sheet.

One more thing that often gets missed: your Sheet ID, the long string of letters and numbers in your Google Sheet URL, should live inside Apps Script. Not in your frontend HTML or JavaScript. Not anywhere a user can see it.

Here is a simple rule for what belongs on GitHub and what does not.

Safe to put on GitHub: your HTML structure, your CSS styling, your display logic, and your Apps Script URL. These are fine to be public.

Not safe to put on GitHub: passwords, API keys from third-party services, your Sheet ID, and any secret token you use for authentication. Once something is committed to GitHub, it lives in the history permanently. Even if you delete it in the next commit, it is still there. Bots scrape GitHub constantly looking for exactly this.

If You Are Using Firebase

You decide Google Sheets is too basic so you move to Firebase, which is a proper database built for apps. You set it up, put the configuration in your frontend code, and it works.

Here is where most people panic: the Firebase configuration contains something called an API key, and it is sitting right there in your code for anyone to read.

It is actually fine. Firebase API keys are designed to be public. Google built it that way on purpose. The API key is just the address of your database. Think of it like a shop address. Knowing the address does not mean you can walk in and take things. What controls who actually gets access is something completely separate, called Firebase Security Rules.

If your Security Rules are not configured, anyone who finds your app can open their browser console and read, write, or delete your entire database without logging in. They do not need to hack anything. They just need to know the Firebase SDK commands, which are publicly documented, and your config, which is already in your code.

This is not a theoretical risk. It is one of the most common mistakes in Malaysian web development right now.

To fix it, go to your Firebase console, find the Rules section under Realtime Database or Firestore, and write rules that restrict access. At minimum, the rules should say that a user must be logged in and can only access data that belongs to them. For example, a parent can only read results linked to their own IC number, not anyone else's. Firebase has a built-in simulator in the console where you can test your rules before going live. Use it every time before you deploy.

If You Are Using Supabase

Supabase is a good option, especially for developers who prefer working with proper structured data. Unlike Firebase which uses a flexible document-style format called NoSQL, Supabase runs on PostgreSQL, which is a real SQL database. More familiar for many developers, and more suited to structured data like student records, customer lists, or appointment bookings.

The security concept works the same way as Firebase Security Rules. Supabase calls it Row Level Security, or RLS. It controls who can see which rows of data. Set it up correctly and a parent can only ever see their own child's results. Other families' records are invisible to them even if they try.

The catch: RLS is turned off by default in every new Supabase project, for every table. You have to go in and enable it yourself. If you forget, all your data is accessible to anyone who has your public key, which is already in your frontend code by design. The platform assumes you will configure RLS. A lot of first-time developers do not realise this until something has already gone wrong.

Before you go live, open your Supabase dashboard, go to each table, and check whether RLS is enabled. If it is not, turn it on and write policies that define exactly who can see what.

One genuine advantage Supabase has over the other two options: it comes with built-in authentication. Login, logout, and session management are already included out of the box. With Google Sheets you have to build this yourself using Apps Script logic. With Firebase you have to configure Firebase Auth separately. With Supabase it is already there. Less to build, less to misconfigure, less to go wrong.

The One Thing All Three Platforms Have in Common

This is the most important point in this entire article, and I want to say it clearly.

Google Sheets, Firebase, and Supabase are all open by default. None of them are automatically secure just because you are using them. Every single one requires you to manually set up and configure the security layer before your app is safe to go live.

The platform gives you the tools. You have to use them. Most developers ship before they do that, not because they do not care, but because nobody told them this step existed.

Now you know.

What Data to Show and What to Hide

Even after everything above, there is one more habit worth building. When your app displays personal data back to the user, do not show the full details on screen.

An IC number should never appear in full. Show it as 800101-**-5555 instead. A phone number should appear as 012-***6789. Only show what the user actually needs to see to confirm they are looking at the right record. Nothing more.

This is called data masking. It protects your users even in simple situations, someone looking over their shoulder, a screenshot being taken, a shared device left unlocked. The full IC number has no reason to appear on a results page. Mask it.

What Malaysian Law Says About All of This

Under PDPA, if you build a system that collects or stores other people's personal data, you are classified as a Data Processor. This applies whether you are a solo freelancer, a small agency, or a developer building something on the side. The law does not ask how experienced you are or how long you have been coding. It asks what happened to the data.

If your system leaks personal data because of how it was built, and the cause was something you could have prevented, you are exposed to legal liability. The 2024 amendments made the obligations more explicit. Security is expected to be built in from the start, not patched after a breach.

I have seen a real case go to court. The forensic report listed every mistake. Open database, API keys and secrets on public GitHub, no access controls, no audit logs. All preventable. None of it was caught before it was too late. The developer thought they were just building an app. The law saw someone who was handling other people's personal data carelessly.

Before you ship anything, ask yourself one question: if someone went looking through my app right now, what could they find?

If that answer makes you uncomfortable, fix it first. Then launch.

Not sure if your current setup is secure? We review developer setups for PDPA compliance before they go live. Reach out to us on WhatsApp and we will tell you exactly what needs fixing.