Przejdź do treści
Infrastruktura & Technologia

Next.js nad Supabase - bezpieczna eksploatacja

Runbook DevOps dla Next.js na Supabase: architektura, middleware, wzorce uwierzytelniania, rate limiting i integracja z Claude Code.

Mansoor Ahmed
Mansoor Ahmed
Head of Engineering 14 min czytania

Po tym jak Supabase jako platforma backendowa działa stabilnie, na niej budowany jest właściwy stack aplikacyjny.

W wielu nowoczesnych projektach Next.js pełni rolę warstwy aplikacyjnej:

  • Frontend Rendering
  • Server Side Rendering
  • Server Actions
  • Route Handlers
  • API Proxy
  • Session Handling

Tym samym Next.js staje się faktycznie bramą backendową między przeglądarką a Supabase.

Najczęstszym błędem jest traktowanie tej warstwy jako “frontendu”, podczas gdy w rzeczywistości zawiera ona logikę serwerową z wysokimi uprawnieniami.

Ten runbook opisuje, jak bezpiecznie eksploatować Next.js nad self-hosted platformą Supabase.

Każda sekcja zawiera:

  • Implementację
  • warunek weryfikowalny
  • Failure Scenario

W skrócie - Artykuł 2 z 6 serii DevOps Runbook

  • Next.js jako osobny kontener odizolowany od Supabase
  • Klucz service_role tylko w jednym pliku
  • Middleware z getUser() przy każdym żądaniu
  • Sprawdzenie ownership przed każdą mutacją
  • Rate limiting na endpointach uwierzytelniania

Spis treści serii

Ta instrukcja jest częścią naszej serii runbooków DevOps dla self-hosted stacków aplikacyjnych.

  1. Supabase Self-Hosting Runbook
  2. Next.js nad Supabase - bezpieczna eksploatacja ← ten artykuł
  3. Supabase Edge Functions - bezpieczne wdrożenie
  4. Bezpieczna obsługa Trigger.dev Background Jobs
  5. Claude Code jako kontrola bezpieczeństwa w DevOps
  6. Security Baseline dla całego stacku

Artykuł 1 opisuje bazę platformową. Ten artykuł opisuje warstwę aplikacyjną powyżej.

Przegląd architektury

Browser (Client)
     |
     | HTTPS
     |
Next.js App Layer
     |
     +-- @supabase/ssr
     |
     +-- service_role client
           (tylko izolowane konteksty administracyjne)
     |
Supabase Platform Layer
     |
     +-- Kong API Gateway
     +-- GoTrue Auth
     +-- PostgREST API
     +-- Realtime
     |
PostgreSQL Data Layer
     |
     +-- Row Level Security

Zasady podstawowe:

Browser    -> komunikuje się tylko z Next.js
Next.js    -> komunikuje się z Supabase
Supabase   -> kontroluje dostęp przez RLS

Gdy przeglądarka komunikuje się bezpośrednio z wieloma usługami backendowymi, powstają niekontrolowalne granice bezpieczeństwa.

Część A - Decyzje architektoniczne

Te decyzje są rzadko zmieniane i stanowią fundament.

A1 - Next.js jako osobna usługa

Implementacja

Next.js działa jako osobny kontener.

services

nextjs-app
supabase-stack
postgres

Przykład docker-compose:

services:

  nextjs-app:
    build: ./app
    ports:
      - "3000:3000"
    environment:
      - NEXT_PUBLIC_SUPABASE_URL=http://kong:8000
      - NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
      - SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY}
    networks:
      - internal

Next.js nie może działać wewnątrz stacku Supabase.

Warunek weryfikowalny

docker ps --format '{{.Names}}'

Oczekiwanie:

nextjs-app
supabase-kong
supabase-postgres
supabase-auth

Failure Scenario

Gdy Next.js działa w tym samym kontenerze co Supabase:

  • przestrzeń procesów jest współdzielona
  • sekrety znajdują się w tym samym środowisku
  • skompromitowany serwer Next.js ma bezpośredni dostęp do wszystkich usług backendowych

A2 - Przeglądarka komunikuje się tylko z Next.js

Implementacja

Przeglądarka może widzieć tylko jeden publiczny URL.

https://app.example.com

Niedozwolone:

https://app.example.com:8000
https://app.example.com:5432
https://app.example.com:9000

Przykład firewalla:

iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8000 -j DROP
iptables -A INPUT -p tcp --dport 5432 -j DROP
iptables -A INPUT -p tcp --dport 9000 -j DROP

Warunek weryfikowalny

nmap -p 443,3000,5432,8000,9000 app.example.com

Oczekiwanie:

443 open
wszystkie inne filtered

Failure Scenario

Gdy Supabase Studio jest publicznie dostępne:

  • pełny dostęp do bazy danych
  • manipulacja schematem
  • dostęp do bucketów Storage

A3 - Ustawienie Security Headers

Next.js pełni rolę bramy i musi ustawiać nagłówki HTTP Security.

Implementacja

next.config.js
const securityHeaders = [
  { key: "X-Frame-Options", value: "DENY" },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "Permissions-Policy", value: "camera=(), microphone=()" },
  { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains" },
  {
    key: "Content-Security-Policy",
    value: "default-src 'self'; img-src 'self' data: blob:; frame-ancestors 'none'"
  }
]

Warunek weryfikowalny

curl -I https://app.example.com

Oczekiwane nagłówki:

X-Frame-Options
Content-Security-Policy
Strict-Transport-Security

Failure Scenario

Bez CSP:

  • ataki XSS ładują zewnętrzne skrypty
  • tokeny mogą być wykradzione

Zmienne środowiskowe

ZmiennaWidocznośćDozwolona lokalizacjaRyzyko wycieku
NEXT_PUBLIC_SUPABASE_URLKlient.env, kod klientaNiskie (publiczny URL)
NEXT_PUBLIC_SUPABASE_ANON_KEYKlient.env, kod klientaŚrednie (ograniczony przez RLS)
SUPABASE_SERVICE_ROLE_KEYTylko serwerlib/supabase/admin.tsKrytyczne (omija RLS)
DATABASE_URLTylko serwer.env (serwer)Krytyczne (bezpośredni dostęp do DB)
TRIGGER_API_KEYTylko serwer.env (serwer)Wysokie (dostęp do kolejki zadań)

Część B - Kontrole implementacji

Te zasady obowiązują przy każdej zmianie kodu.

B1 - Separacja zmiennych środowiskowych

Widoczne dla klienta:

NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY

Tylko serwer:

SUPABASE_SERVICE_ROLE_KEY
DATABASE_URL
TRIGGER_API_KEY

Warunek weryfikowalny

grep -r "NEXT_PUBLIC_" .env*

Sekrety nie mogą tam występować.

Build Leak Check

grep -r "SERVICE_ROLE" .next/

Oczekiwanie:

brak wyników

Failure Scenario

service_role w bundlu klienta oznacza:

  • pełny dostęp do bazy danych
  • RLS całkowicie nieskuteczny

Kto zna podstawy zarządzania sekretami, rozumie dlaczego ta separacja jest niezbędna.

B2 - Prawidłowa konfiguracja Supabase SSR Client

Server Client:

import { createServerClient } from "@supabase/ssr"

Serwer używa anon key, nie service_role.

Admin Client:

createClient(url, SERVICE_ROLE_KEY)

Tylko do zadań administracyjnych.

Warunek weryfikowalny

grep -rn "SERVICE_ROLE" app/

Oczekiwanie: tylko w

lib/supabase/admin.ts

Failure Scenario

Server Client z service_role:

  • RLS jest całkowicie omijany
  • wszystkie żądania mają uprawnienia administratora

B3 - Middleware dla Auth i Token Refresh

Middleware działa przed każdym żądaniem.

middleware.ts

Implementacja

const { data: { user } } = await supabase.auth.getUser()

Nie:

getSession()

Warunek weryfikowalny

grep getUser middleware.ts

Failure Scenario

Bez middleware:

  • Token Refresh nie działa
  • sesja wygasa po 1 godzinie

B4 - Wzorzec mutacji

Wszystkie mutacje przebiegają według tego samego schematu.

Auth
Input Validation
Ownership Check
Mutation
Logging

Przykład

const { user } = await supabase.auth.getUser()

if (!user) return

const post = await supabase
  .from("posts")
  .select("user_id")
  .eq("id", postId)

if (post.user_id !== user.id)
  throw new Error("forbidden")

Failure Scenario

Bez Ownership Check:

Statystyka: Zgodnie z badaniami GitHuba z 2024 roku ponad 23% wycieków sekretów w projektach webowych dotyczy kluczy service_role lub równoważnych tokenów backendowych.

deletePost(id)

Użytkownik może usunąć cudze dane.

B5 - Rate Limiting

Next.js nie ma wbudowanego Rate Limitingu.

Zalecane rozwiązanie:

Upstash Redis

Przykład

10 requests / minute / IP

Warunek weryfikowalny

for i in {1..20}
do curl -X POST /api/login
done

Oczekiwanie:

HTTP 429

B6 - Logowanie bez sekretów

Logi nie mogą zawierać:

JWT Tokens
service_role keys
emails
passwords

Warunek weryfikowalny

grep console.log app/

Część C - Eksploatacja

C1 - Aktualizacje zależności

Wydania Next.js są częste.

Cotygodniowa weryfikacja:

npm audit
npm outdated

Zalecane:

Renovate / Dependabot

C2 - Integracja Claude Code

Architektura:

Git Push
   |
Kontrole deterministyczne
   |
Raport bezpieczeństwa
   |
Analiza Claude
   |
Decyzja DevOps

Kontrole deterministyczne

grep service_role
grep NEXT_PUBLIC
npm audit

Analiza Claude

Claude weryfikuje:

  • nowe Server Actions
  • nowe API Routes
  • wzorce Ownership
  • Input Validation
  • dryft architektury

Claude nie wykonuje żadnych zmian na produkcji.

Lista kontrolna wdrożenia

Przed każdym wdrożeniem sprawdzić:

[ ] Next.js działa jako osobna usługa
[ ] porty Supabase zamknięte zewnętrznie
[ ] Security Headers aktywne

[ ] brak service_role w bundlu klienta
[ ] service_role tylko w Admin Client

[ ] middleware.ts obecny
[ ] Server Actions weryfikują Auth
[ ] Ownership Checks zaimplementowane

[ ] Rate Limit logowania aktywny

[ ] npm audit bez krytycznych wyników

Podsumowanie

Next.js w nowoczesnym stacku nie jest frontendem, lecz uprzywilejowaną warstwą serwerową.

Bezpieczeństwo powstaje na trzech poziomach:

Architektura
Kontrole implementacji
bieżące audyty

Kombinacja kontroli bezpieczeństwa CI i analizy Claude Code wykrywa zarówno znane wzorce, jak i nowe zagrożenia.

Kto stosuje te zasady razem z architekturą Cert-Ready by Design, buduje weryfikowalne bezpieczeństwo zamiast audytów po fakcie.

Pobierz listę kontrolną audytu

Przygotowany prompt dla Claude Code. Prześlij plik na swój serwer i uruchom Claude Code w katalogu projektu aplikacji Next.js. Claude Code automatycznie sprawdzi wszystkie punkty bezpieczeństwa z tego runbooka i zgłosi ZALICZONY, OSTRZEŻENIE lub KRYTYCZNY.

claude -p "$(cat claude-check-artikel-2-nextjs-pl.md)" --allowedTools Read,Grep,Glob,Bash

Pobierz listę kontrolną

Spis treści serii

  1. Supabase Self-Hosting Runbook
  2. Next.js nad Supabase - bezpieczna eksploatacja ← ten artykuł
  3. Supabase Edge Functions - bezpieczne wdrożenie
  4. Bezpieczna obsługa Trigger.dev Background Jobs
  5. Claude Code jako kontrola bezpieczeństwa w DevOps
  6. Security Baseline dla całego stacku

Następny artykuł opisuje, jak bezpiecznie wdrożyć Supabase Edge Functions - bez budowania drugiej architektury backendowej.

Bert Gogolin

Bert Gogolin

Dyrektor Generalny, Gosign

AI Governance Briefing

Enterprise AI, regulacje i infrastruktura - raz w miesiącu, bezpośrednio ode mnie.

Bez spamu. Możliwość rezygnacji w każdej chwili. Polityka prywatności

Next.js Supabase Security DevOps Self-Hosting
Udostępnij artykuł

Najczęściej zadawane pytania

Dlaczego Next.js nie może działać w tym samym kontenerze co Supabase?

Gdy Next.js działa w tym samym kontenerze co Supabase, przestrzeń procesów jest współdzielona, a sekrety znajdują się w tym samym środowisku. Skompromitowany serwer Next.js ma wtedy bezpośredni dostęp do wszystkich usług backendowych.

Dlaczego klucz service_role nie może być używany w kodzie klienta?

Klucz service_role omija wszystkie polityki Row Level Security. Jeśli trafi do bundla klienta, każdy użytkownik uzyskuje pełny dostęp do bazy danych, a RLS staje się całkowicie nieskuteczny.

Co się stanie, jeśli Server Action nie ma kontroli uwierzytelniania?

Bez kontroli getUser() w Server Action każdy użytkownik z prawidłowym cookie sesji może wywołać akcję, nawet dla danych innych użytkowników. Ponieważ Server Actions są dostępne przez HTTP POST, prosty curl z wykradzionym cookie wystarczy.