Lyna
SupportBlog
IntroductionFeaturesIntegrationsTips & TricksChangelog

Security Best Practices

Security is not a feature you add at the end. These practices should be part of your workflow from day one.


Never expose API keys in frontend code

This is the most common security mistake. API keys, database credentials, and secrets must never appear in client-side code.

What happens: A secret hardcoded in a React component gets included in the JavaScript bundle sent to every visitor. Anyone with DevTools can find it.

What to do instead:

  • Store secrets in environment variables on the server side
  • Use Lyna's Secrets panel in project settings for server-only variables
  • Prefix client-safe variables with NEXT_PUBLIC_, but only for values designed to be public (like the Supabase anon key)
// WRONG - secret exposed to the browser
const stripe = new Stripe("sk_live_abc123...");

// RIGHT - secret only on the server
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

Pre-publish security scan

Lyna scans your project for exposed secrets before publishing. The scanner checks for:

Secret patterns (critical/high): Stripe keys (sk_live_, sk_test_), AWS access keys (AKIA...), AWS secret keys, Firebase API keys, private key blocks (-----BEGIN PRIVATE KEY-----), JWT secrets, hardcoded passwords, database connection strings (postgres://, mysql://, mongodb://), and generic secret assignments.

Frontend patterns: eval() usage, innerHTML assignment, document.write(), sensitive data in localStorage, unsanitized dangerouslySetInnerHTML, and hardcoded API endpoints.

Database security: RLS policies, table permissions, and auth configuration.

Fix all flagged issues before deploying.


Use Edge Functions for sensitive operations

Anything involving secret keys, third-party API calls, or sensitive business logic should run server-side.

Use Edge Functions for:

  • Payment processing (Stripe webhooks, payment intents)
  • Sending emails via third-party services
  • External API calls requiring authentication
  • Any computation involving secret keys

Example prompt:

"Create a Supabase Edge Function for Stripe webhooks. Verify the signature using STRIPE_WEBHOOK_SECRET, process checkout.session.completed events, and update subscription status in the database."


Row-Level Security (RLS)

RLS controls who can read and write data at the database level. Without policies, tables are either fully public or fully locked down.

The rules

  1. Enable RLS on every table. Ask the AI to do this when creating tables.
  2. Create explicit policies for every operation. Define who can SELECT, INSERT, UPDATE, and DELETE.
  3. Use auth.uid() to scope policies to the authenticated user.
  4. Test your policies. Access data as different users to verify.

Common patterns

Users can only read their own data:

CREATE POLICY "Users can view own data"
ON profiles FOR SELECT
USING (auth.uid() = user_id);

Users can only update their own data:

CREATE POLICY "Users can update own data"
ON profiles FOR UPDATE
USING (auth.uid() = user_id);

Public read, authenticated write:

CREATE POLICY "Public read access"
ON posts FOR SELECT
USING (true);

CREATE POLICY "Authenticated users can insert"
ON posts FOR INSERT
WITH CHECK (auth.uid() IS NOT NULL);

Testing RLS

After setting up policies:

  1. Query as an unauthenticated user. Only public data should be visible.
  2. Query as an authenticated user. Only their data (or data they have access to) should show.
  3. Attempt writes that should be blocked. They should return empty results, not errors.

Gotcha

If your queries return empty results unexpectedly, check RLS first. Missing policies silently block access rather than throwing errors.


Environment secrets management

How Lyna handles secrets

The Secrets panel in project settings stores environment variables that are:

  • Encrypted at rest
  • Server-side only (unless prefixed with NEXT_PUBLIC_)
  • Not included in client JavaScript bundles
  • Not visible in the code editor or version control

Best practices

  • Never commit .env files to Git. Lyna handles this automatically, but add .env* to .gitignore if working with GitHub.
  • Use separate keys for dev and production. Most services (Stripe, Supabase) provide test and live keys.
  • Rotate keys if compromised. Update in the provider's dashboard and in Lyna's Secrets panel.
  • Minimize secrets count. Each secret is an attack surface.

Authentication best practices

Use Supabase Auth

Lyna integrates natively with Supabase Auth. Use it. Rolling your own auth is one of the most error-prone things in web development.

Session management

  • Validate sessions server-side for protected routes
  • Implement session expiry and refresh token rotation
  • Never store session tokens in localStorage. Supabase Auth uses cookies, which is correct for Next.js.

Protected routes

Make sure pages requiring auth check the session server-side:

"Add auth checks to all dashboard routes. If the user is not authenticated, redirect to login. Use server-side checks, not client-side only."

Input validation

Validate all user input server-side, even with client-side validation. Client validation is UX; server validation is the security boundary.

  • Zod schemas for tRPC procedures and API routes
  • Sanitize user content rendered as HTML to prevent XSS
  • Validate file uploads: check MIME types and file sizes on the server

Additional measures

Content Security Policy

For production, add a CSP header to control which scripts, styles, and resources can load:

"Add a Content Security Policy header in Next.js middleware. Allow scripts and styles from our domain and Supabase, block inline scripts and external sources."

Rate limiting

  • Rate limit login attempts
  • Rate limit expensive API operations
  • Supabase has built-in rate limiting for auth endpoints

HTTPS

All Lyna-deployed apps use HTTPS by default. Custom domains get SSL automatically. Never serve content over plain HTTP.


Security checklist

Before publishing:

  • No API keys or secrets in frontend code
  • RLS enabled on all Supabase tables with appropriate policies
  • Auth checks on all protected routes (server-side)
  • Secrets stored in Lyna's Secrets panel, not in code
  • User input validated server-side
  • File uploads validated for type and size
  • Pre-publish security scan passes with no critical issues
  • Sensitive operations use Edge Functions or server routes