Back to Blog
Production GuideSan Francisco, CA

Part 2: Web App Security Basics Every Non-Developer Must Know

Jan 12, 202610 min read
Part 2: Web App Security Basics Every Non-Developer Must Know

🔐 Security Isn't Optional—It's Survival

You've built your app. Users are signing up. Revenue is flowing.

Then one morning, you wake up to this email:

"We noticed unusual activity in your database. All user records have been accessed by an unknown party."

This isn't hypothetical. It happens to startups every week. And in most cases, the vulnerability was embarrassingly simple to prevent.

In Part 1, we covered the deployment checklist. Now let's dive deep into the security items that can make or break your business.


🚨 The 5 Most Common Security Mistakes

1. Exposing API Keys in Frontend Code

The Problem: When you build with AI tools, they often generate code like this:

// ❌ DANGER: This key is visible to EVERYONE const supabase = createClient( 'https://abc.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6...', );

Why it's dangerous: Anyone can open Chrome DevTools → Sources tab and see this key. With it, they can:

  • Read all your data
  • Delete all your data
  • Impersonate any user

The Fix:

  1. Use environment variables:
// ✅ SAFE: Key is loaded from environment const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, );
  1. Understand the difference:
Key TypeSafe in Frontend?Use Case
anon key✅ Yes (with RLS)Client-side queries
service_role key❌ NEVERServer-side only

💡 Rule of thumb: If a key starts with service_role, it should NEVER appear in frontend code.


2. Disabled Row Level Security (RLS)

The Problem: Supabase makes it easy to disable RLS during development. Many founders forget to turn it back on.

-- This is what "RLS Disabled" looks like -- Anyone can SELECT * FROM users and get EVERYTHING

The Real-World Impact:

  • User emails, passwords (hashed, but still), personal data—all exposed
  • Competitors can scrape your entire database
  • GDPR/CCPA violations = massive fines

The Fix:

  1. Enable RLS on every table:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
  1. Create policies that make sense:
-- Users can only read their own data CREATE POLICY "Users can view own data" ON users FOR SELECT USING (auth.uid() = id); -- Users can only update their own data CREATE POLICY "Users can update own data" ON users FOR UPDATE USING (auth.uid() = id);
  1. Test it:
-- Try to select without being logged in -- Should return 0 rows if RLS is working SELECT * FROM users;

3. No Server-Side Validation

The Problem: AI-generated apps often validate inputs only on the frontend.

// Frontend validation only ❌ if (price < 0) { alert("Price can't be negative!"); return; } // Hacker opens DevTools, modifies the request, bypasses this entirely

The Fix: Always validate on the server. Frontend validation is for UX, not security.

// Supabase Edge Function example ✅ export async function POST(req) { const { price } = await req.json(); // Server-side validation - can't be bypassed if (typeof price !== 'number' || price < 0 || price > 10000) { return new Response('Invalid price', { status: 400 }); } // Safe to proceed await supabase.from('products').insert({ price }); }

4. Insecure Authentication Redirects

The Problem: After OAuth login, your app redirects users. If not configured properly, attackers can redirect users to malicious sites.

https://yourapp.com/auth/callback?redirect=https://evil-site.com

The Fix:

  1. Whitelist allowed redirect URLs:
const ALLOWED_REDIRECTS = ['https://yourapp.com', 'https://www.yourapp.com']; function safeRedirect(url) { const isAllowed = ALLOWED_REDIRECTS.some((allowed) => url.startsWith(allowed), ); return isAllowed ? url : '/dashboard'; }
  1. Configure in Supabase Dashboard:
    • Go to Authentication → URL Configuration
    • Add only your exact production domains
    • Remove localhost before going live (or use a separate project)

5. Logging Sensitive Data

The Problem: During debugging, you might log everything:

// ❌ This goes to your logging service, potentially visible to employees console.log('User login:', { email, password, creditCard });

The Fix:

// ✅ Only log what you need, never sensitive data console.log('User login attempt:', { email: user.email, timestamp: new Date().toISOString(), }); // Never log: passwords, tokens, credit cards, SSNs

🛡️ The Security Checklist

Before you launch, run through this:

Environment & Keys

  • All API keys are in environment variables
  • service_role key is ONLY used server-side
  • .env files are in .gitignore
  • Production keys are different from development keys

Database

  • RLS is enabled on ALL tables
  • Each table has appropriate SELECT/INSERT/UPDATE/DELETE policies
  • Tested: Unauthenticated requests return empty/error

Authentication

  • Redirect URLs are whitelisted
  • OAuth providers have production domains configured
  • Session tokens expire appropriately
  • Password reset flow is tested

Code

  • All user inputs are validated server-side
  • No sensitive data in logs
  • Error messages don't leak internal details

🤔 "This Is a Lot. Where Do I Start?"

We get it. Security is complex, and getting it wrong can destroy your business.

Here's the priority order:

  1. 🔴 Critical (Do Today): API keys + RLS
  2. 🟠 High (This Week): Server-side validation
  3. 🟡 Medium (Before Launch): Auth redirects + logging audit

Or, let us handle it:


Security Audit Included

Every ShipTheProduct engagement includes a full security review. We check your keys, RLS policies, and auth flow before you launch.

See Pricing

📚 Series Navigation

PartTitleStatus
1The 7-Point Deployment Checklist
2Web App Security Basics (This Post)
36 Performance Optimization Points for Production🔜 Coming Soon
4Database Design Guide for Non-Developers🔜 Coming Soon

Next up: Performance optimization—because a secure app that takes 10 seconds to load is still a failed app.

Previous: Part 1: The 7-Point Deployment Checklist


*Found a security issue in your app? Don't panic. Reach out: hello@shiptheproduct.dev*