ScaleRocket/Web

Vue d'ensemble de la sécurité

En-têtes de sécurité, CORS, validation des entrées, gestion des erreurs et checklist de production.

Vue d'ensemble

ScaleRocket suit les bonnes pratiques de sécurité à tous les niveaux -- base de données (RLS), API (Edge Functions) et client (gardes d'authentification). Cette page couvre l'architecture de sécurité et ce qu'il faut vérifier avant de passer en production.

Architecture de sécurité

Client (Browser)
  ├── Auth Guard (route protection)
  ├── Supabase client (anon key, JWT)
  └── HTTPS only

Edge Functions (Server)
  ├── JWT validation
  ├── CORS headers
  ├── Input validation
  ├── Stripe signature verification
  └── Service role (bypasses RLS)

Database (Supabase)
  ├── Row Level Security (RLS)
  ├── Triggers (server-side logic)
  └── Functions (SECURITY DEFINER)

En-têtes de sécurité

Le site marketing (apps/web) inclut des en-têtes de sécurité complets dans next.config.ts :

En-têteValeurObjectif
X-Frame-OptionsDENYEmpêche le clickjacking
X-Content-Type-OptionsnosniffEmpêche le reniflage de type MIME
Referrer-Policystrict-origin-when-cross-originContrôle les informations de referrer
X-DNS-Prefetch-ControlonActive le DNS prefetching pour la performance
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS pendant 1 an
Permissions-Policycamera=(), microphone=(), geolocation=()Désactive les API navigateur inutilisées
Content-Security-PolicyVoir ci-dessousEmpêche les attaques XSS et d'injection

Content Security Policy (CSP)

L'en-tête CSP est l'en-tête de sécurité le plus important. Il indique au navigateur quelles sources de contenu sont autorisées :

// apps/web/next.config.ts
{
  key: "Content-Security-Policy",
  value: [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://*.supabase.co",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: blob: https://*.supabase.co https://*.stripe.com",
    "connect-src 'self' https://*.supabase.co https://api.stripe.com https://*.resend.com",
    "frame-src https://js.stripe.com https://*.supabase.co",
    "object-src 'none'",
    "base-uri 'self'",
    "form-action 'self'",
    "frame-ancestors 'none'",
  ].join("; "),
}

Personnaliser la CSP : Si vous ajoutez des scripts tiers (analytics, widgets de chat), ajoutez leurs domaines à la directive appropriée. Par exemple, pour autoriser Plausible analytics, ajoutez https://plausible.io à script-src et connect-src.

Pour les applications Vite (apps/app, apps/ops), configurez ces en-têtes dans votre plateforme d'hébergement (configuration vercel.json ou headers de Vercel).

Configuration CORS

Les Edge Functions utilisent la variable d'environnement APP_URL pour le CORS, limitant les requêtes à votre domaine applicatif uniquement :

// supabase/functions/_shared/supabase.ts
const allowedOrigin = Deno.env.get("APP_URL") || "http://localhost:5173";

export const corsHeaders = {
  "Access-Control-Allow-Origin": allowedOrigin,
  "Access-Control-Allow-Headers":
    "authorization, x-client-info, apikey, content-type",
};

Définissez APP_URL dans vos secrets Supabase pour la production :

npx supabase secrets set APP_URL=https://app.yourdomain.com

Note : La fonction stripe-webhook utilise un gestionnaire CORS séparé car les webhooks Stripe n'envoient pas d'en-tête d'origine. La sécurité des webhooks repose sur la vérification de signature, pas sur le CORS.

Validation des entrées

Validez toutes les entrées dans les Edge Functions avant le traitement :

Deno.serve(async (req) => {
  const { priceId, mode } = await req.json();

  // Validate required fields
  if (!priceId || typeof priceId !== "string") {
    return new Response(
      JSON.stringify({ error: "Invalid priceId" }),
      { status: 400 }
    );
  }

  // Validate against allowed values
  if (!["subscription", "payment"].includes(mode)) {
    return new Response(
      JSON.stringify({ error: "Invalid mode" }),
      { status: 400 }
    );
  }

  // Proceed with validated data
});

Gestion des erreurs

Ne jamais exposer les erreurs internes au client :

try {
  // Function logic
} catch (error) {
  // Log the full error server-side
  console.error("Function failed:", error);

  // Return a generic message to the client
  return new Response(
    JSON.stringify({ error: "An unexpected error occurred" }),
    { status: 500, headers: corsHeaders }
  );
}

Règles de sécurité clés

  1. Ne jamais exposer la clé service role dans apps/web ou apps/app. Elle est réservée à apps/ops et aux Edge Functions.
  2. Toujours activer le RLS sur les nouvelles tables. Une table sans RLS permet à quiconque possédant la clé anon de lire toutes les lignes.
  3. Toujours valider le JWT dans les Edge Functions protégées en utilisant le helper getUser().
  4. Toujours vérifier les signatures des webhooks Stripe pour empêcher les événements falsifiés.
  5. Utiliser SECURITY DEFINER avec parcimonie. Les fonctions avec ce flag s'exécutent en tant que propriétaire (superuser), contournant le RLS.

Checklist de sécurité pour la production

Avant de déployer en production :

  • RLS activé sur chaque table avec les politiques appropriées
  • CORS restreint à vos domaines réels (pas *)
  • Clé service role uniquement dans l'application admin et les secrets des Edge Functions
  • Secret webhook Stripe défini dans les secrets Supabase
  • En-têtes de sécurité configurés sur toutes les applications
  • URLs de redirection OAuth restreintes à vos domaines dans le dashboard Supabase
  • Templates d'e-mails personnalisés (pas de branding Supabase par défaut)
  • Liste blanche admin mise à jour avec les vrais e-mails admin
  • Rate limiting activé dans le dashboard Supabase
  • Sauvegardes de base de données activées (plan Supabase Pro)
  • Mode test Stripe basculé en mode live
  • Variables d'environnement avec les valeurs de production (pas de clés de test)

Fini ? Marquez cette page comme terminée.

On this page