SupabaseMedium

Edge Function Security Issue

Supabase Edge Functions lacking proper authentication checks, input validation, or error handling expose backend logic to abuse.

Last updated 2026-01-15

What Is This Vulnerability

Supabase Edge Functions are serverless Deno functions that run on the edge. They are invoked via HTTP and often serve as the backend logic layer for operations that require server-side processing, such as payment handling, email sending, or third-party API calls. When these functions do not properly verify the caller's identity, validate input, or handle errors securely, they become an attack vector.

Common issues include:

  • Missing JWT verification on the incoming Authorization header
  • Using the anonymous key instead of verifying a real user session
  • Not validating or sanitizing input parameters
  • Returning verbose error messages that leak internal details
// VULNERABLE: No authentication check
Deno.serve(async (req) => {
  const { userId, action } = await req.json();
  // Directly uses user-provided userId without verifying JWT
  const { data } = await supabaseAdmin
    .from('user_data')
    .select('*')
    .eq('user_id', userId);
  return new Response(JSON.stringify(data));
});

Why It's Dangerous

Vulnerable Edge Functions can lead to:

  • Authentication bypass: Callers can impersonate any user by providing arbitrary user IDs
  • Unauthorized access to service_role operations: Edge Functions typically use the service_role key, which bypasses RLS. Without auth checks, this power is available to anyone
  • Data exfiltration: Functions that query data based on user-provided parameters can be exploited to extract sensitive information
  • Business logic abuse: Functions handling payments, credits, or subscriptions can be manipulated
  • Information disclosure: Verbose error messages may reveal database structure, internal API endpoints, or environment variable names

Since Edge Functions operate with the service_role key, they have full database access. Any function without proper auth is essentially granting service_role-level access to the caller.

How to Detect

Review each Edge Function for authentication checks. The standard pattern should verify the JWT from the Authorization header:

// Check for this pattern in every Edge Function
const authHeader = req.headers.get('Authorization');
if (!authHeader) {
  return new Response('Unauthorized', { status: 401 });
}
const { data: { user }, error } = await supabase.auth.getUser(
  authHeader.replace('Bearer ', '')
);
if (error || !user) {
  return new Response('Unauthorized', { status: 401 });
}

AuditYourApp calls each discovered Edge Function endpoint without an Authorization header and with an anonymous key to check if authentication is properly enforced. It also tests with modified JWTs to detect functions that trust the token without verification.

How to Fix

Implement proper authentication in every Edge Function:

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';

Deno.serve(async (req) => {
  // CORS handling
  if (req.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  // Verify authentication
  const authHeader = req.headers.get('Authorization');
  if (!authHeader) {
    return new Response(
      JSON.stringify({ error: 'Missing authorization header' }),
      { status: 401, headers: corsHeaders }
    );
  }

  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_ANON_KEY')!,
    { global: { headers: { Authorization: authHeader } } }
  );

  const { data: { user }, error: authError } = await supabase.auth.getUser();
  if (authError || !user) {
    return new Response(
      JSON.stringify({ error: 'Invalid token' }),
      { status: 401, headers: corsHeaders }
    );
  }

  // Now use user.id instead of user-provided ID
  const { data } = await supabaseAdmin
    .from('user_data')
    .select('*')
    .eq('user_id', user.id);

  return new Response(JSON.stringify(data), { headers: corsHeaders });
});

Key principles: always verify the JWT server-side using getUser(), never trust client-provided user IDs, validate all input parameters, and return generic error messages to prevent information disclosure.

Scan your app for this vulnerability

AuditYourApp automatically detects security misconfigurations in Supabase and Firebase projects. Get actionable remediation in minutes.

Run Free Scan