FixVibe

// docs / security guides / hardening

Как защитить приложение, построенное с помощью AI-инструментов кодирования

Пошаговое руководство по усилению защиты приложений, созданных вами с помощью Cursor, Claude Code, Lovable, Bolt, v0, Replit или Windsurf. Четыре этапа: понять, почему приложения, созданные AI-, выходят из строя по-разному, провести немедленный аудит кодовой базы, усилить защиту во время развертывания, а затем продолжить мониторинг. Умный, повествовательный, с реальными фрагментами, которые можно копировать.

Почему приложения, созданные AI-, выходят из строя по-разному

Приложения с кодом Vibe могут быть безопасными. Им нужен дополнительный проход аудита, потому что виды сбоев являются структурными, а не небрежными:

  • Assistants inline hardcoded keys. You ask for a fix to an auth error and get a pasted Supabase example that assumes a service-role client. The key ends up at the top of a page component. Both the anon client and the service client coexist; both ship.
  • Generated servers default to permissive CORS. Generated Express / Fastify handlers often ship with cors({ origin: '*' }) because that's the fastest way to get a working preview. The middleware never gets a second pass.
  • Rules files get skipped. Firestore-backed projects generate the data model but rarely touch firestore.rules. Test-mode rules let anyone read and overwrite data until someone replaces them.
  • RLS never enters the migration. A generated Supabase schema and CRUD surface use the anon key, but ENABLE ROW LEVEL SECURITY never enters the migration. Anonymous users can read or write any row.
  • Handlers trust IDs. A generated GET /api/items/[id] reads the param and queries Postgres without verifying ownership. Active scans on a verified domain test for this (IDOR / BOLA).

Немедленный аудит: просмотрите свою кодовую базу на наличие шаблонов риска

Прежде чем что-либо укреплять, найдите то, что уже сломано. Каждая из этих операций занимает менее минуты:

Секреты и ключи провайдера

bash
grep -RIn 'NEXT_PUBLIC_SUPABASE_SERVICE' src/
grep -RIn 'sk_live_\|pk_live_\|STRIPE_SECRET' src/
grep -RIn 'sk-ant-\|^sk-' src/  # Anthropic / OpenAI
grep -RIn 'AIza\|AKIA' src/        # Google / AWS
grep -RIn 'eyJh[A-Za-z0-9_-]\{20,\}' src/  # JWT-shaped strings

Любое попадание требует удаления плюс ротации клавиш. Панели Provider: Supabase → Настройки → API, Stripe → Разработчики → клавиши API, консоль Anthropic/OpenAI.

Контроль доступа к базе данных

bash
# Supabase migrations
grep -RIn 'CREATE TABLE public\.' supabase/migrations/
grep -RIn 'ENABLE ROW LEVEL SECURITY\|FORCE ROW LEVEL SECURITY' supabase/migrations/

# Firebase / Firestore
cat firestore.rules  # confirm no `if true;` matches

Каждому CREATE TABLE public.* требуется соответствующий ENABLE ROW LEVEL SECURITY и хотя бы одна политика. Правила Firestore должны ограничивать чтение request.auth.uid.

Аутентификация и обработка сеанса

bash
grep -RIn 'getSession()' src/   # should be getUser() server-side
grep -RIn 'localStorage\.\(set\|get\)Item.*token' src/
grep -RIn 'jwt.verify.*\(noVerify\|skipVerify\)' src/

Маршруты, отображаемые сервером, должны использовать supabase.auth.getUser() — он проверяется на бэкэнде. getSession() считывает непроверенный файл cookie. Токены в localStorage доступны любому скрипту, который выполняется на странице.

Заголовки и промежуточное программное обеспечение

bash
# Confirm middleware location for src/ layouts
ls src/middleware.ts middleware.ts 2>&1

# Look for CSP and security headers
grep -RIn 'Content-Security-Policy\|Strict-Transport-Security' src/

При раскладке src/ подхватывается только src/middleware.ts. Если ваш файл промежуточного программного обеспечения находится в корне проекта, Next.js молча игнорирует его, и ваша логика CSP / auth-refresh никогда не запускается.

Усиление во время развертывания

Как только исходный код будет очищен, заблокируйте путь приложения к рабочей версии.

Шаг 1. Разделение сред

Vercel: три среды — Production (ваш домен продукта), Предварительная версия (PR / промежуточные развертывания), Разработка (локальная). Каждый получает свой собственный набор env-var. Живые ключи Stripe / Anthropic / Supabase никогда не доходят до предварительного просмотра; Ключи предварительного просмотра никогда не достигают Production. Ветки автоматически переходят в режим предварительного просмотра; слияние с main развертывается с Production.

Шаг 2: Строгое CSP через промежуточное программное обеспечение

Сгенерируйте одноразовый номер для каждого запроса, а затем внедрите его в Content-Security-Policy. Next.js автоматически применяет nonce к своим собственным тегам сценария, когда вы устанавливаете заголовок запроса x-nonce.

ts
// src/middleware.ts
import { NextResponse, type NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const nonce = crypto.randomUUID().replace(/-/g, '');
  const csp = [
    `script-src 'nonce-${nonce}' 'strict-dynamic'`,
    `style-src 'self' 'unsafe-inline'`,
    `img-src 'self' data: https:`,
    `connect-src 'self' https://*.supabase.co`,
    `object-src 'none'`,
    `base-uri 'self'`,
    `frame-ancestors 'none'`,
  ].join('; ');

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);

  const response = NextResponse.next({ request: { headers: requestHeaders } });
  response.headers.set('Content-Security-Policy', csp);
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  return response;
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Шаг 3. Принудительно установите RLS на каждой общедоступной таблице.

RLS isn't enabled by default on tables you create in SQL or migrations. Enable it on every exposed table and pair each one with explicit policies per role — that is what stops the anon and authenticated roles. FORCE only makes the table owner obey RLS too.

sql
-- supabase/migrations/XXXX_rls.sql
alter table public.profiles enable row level security;
alter table public.profiles force row level security;

create policy "profiles: read own"
  on public.profiles for select
  using (auth.uid() = id);

create policy "profiles: update own"
  on public.profiles for update
  using (auth.uid() = id)
  with check (auth.uid() = id);

Шаг 4. Проверка подлинности только на сервере на каждом маршруте API

Каждый маршрут API, изменяющий состояние, проверяет серверную сторону вызывающего объекта с помощью supabase.auth.getUser(). Пользовательский объект становится источником истины для user_id — никогда не доверяйте его установку телу запроса.

ts
// src/app/api/items/route.ts
import { NextResponse, type NextRequest } from 'next/server';
import { createClient } from '@/lib/supabase/server';

export async function POST(request: NextRequest) {
  const supabase = await createClient();
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: 'unauthorized' }, { status: 401 });

  const body = await request.json();
  const { data, error } = await supabase
    .from('items')
    .insert({ ...body, user_id: user.id })  // server-supplied, not from body
    .select()
    .single();

  if (error) return NextResponse.json({ error: error.message }, { status: 400 });
  return NextResponse.json(data);
}

Шаг 5. Обратное проксирование вашей аналитики

Proxying аналитика через ваш собственный домен позволяет избежать блокировки рекламы и позволяет вашему CSP connect-src 'self' оставаться узким. Тот же шаблон работает для PostHog, Plausible, Umami, пользовательских приемников событий.

ts
// src/app/api/posthog/[...path]/route.ts
import { type NextRequest } from 'next/server';

const UPSTREAM = 'https://us.i.posthog.com';

export async function POST(req: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
  const { path } = await params;
  const url = `${UPSTREAM}/${path.join('/')}`;
  return fetch(url, {
    method: 'POST',
    headers: { 'content-type': req.headers.get('content-type') ?? 'application/json' },
    body: await req.text(),
  });
}

Шаг 6. Защита открытого перенаправления при возврате после аутентификации

Потоки входа/регистрации обычно принимают параметр запроса next. Отклоняйте все, что не является путем к тому же сайту — начните с / и никогда не // (относительно протокола, отправляет пользователей за пределы сайта).

ts
function safeNext(raw: string | null): string {
  if (!raw) return '/dashboard';
  if (!raw.startsWith('/') || raw.startsWith('//')) return '/dashboard';
  return raw;
}

Продолжается: мониторинг и повторное сканирование

Дрифт происходит при каждом развертывании. Относитесь к безопасности как к циклу, а не как к контрольному списку, который вы выполняете.

Подтвердите свой рабочий домен

Dashboard → Domains → add your production domain → DNS TXT or HTTP-file verification. Active scans require Hobby or above; scheduled re-scans require Pro or Unlimited.

Запланируйте пассивное повторное сканирование

Scheduled re-scans are available on Pro and Unlimited for verified domains. Free and Hobby scans are manual. Scheduled scans use your plan allowance. Configure completion email preferences and a scan.completed webhook if needed.

bash
# Or from CI, via the REST API:
curl -X POST https://fixvibe.app/api/v1/scans \
  -H "authorization: Bearer $FIXVIBE_TOKEN" \
  -H "content-type: application/json" \
  -d '{"target":"https://your-app.com"}'

Включить API-активное сканирование (необязательно)

Если вы хотите автоматическое активное зондирование (SQLi / XSS / IDOR ходьба / и т. д.), включите его для каждого домена в Личном кабинете → Домены → API активный. Разрешение является долгосрочным, сроком действия 90 дней и возможностью немедленного отзыва. Выполните сопряжение с веб-перехватчиком scan.active_api.first_used, чтобы первое автоматическое активное сканирование после включения достигло вашего оповещения.

Включите полученные результаты в свой рабочий процесс AI

On Hobby or above, create an API token at Account → API tokens and configure the MCP server (/docs/mcp) in your coding tool. Ask your agent to run an authorized scan and inspect the highest-severity findings. Code fixes can use remediation prompts; provider and DNS fixes may need manual operator steps.

Обнаружение угроз в реальном времени (Unlimited)

Periodic certificate-transparency, DNS, JS-bundle, and threat-intelligence checks report observed changes on supported signals. Alerts depend on successful polling and source availability; they do not establish continuous or complete security coverage.

Реальные модели сбоев и их исправления

Five common patterns in AI-generated apps, each with the actual fix:

  1. Ключ роли службы в клиентском компоненте

    Symptom: FixVibe reports an exposed Supabase service-role key on the production URL. Cause: an autocomplete pasted createClient(URL, SERVICE_ROLE_KEY) into a React component. Fix: move the service client to src/lib/supabase/service.ts with import 'server-only' at the top; create a parallel src/lib/supabase/client.ts using the anon key for client-side use; rotate the service-role key via Supabase Studio.

  2. Правила Firestore остались в тестовом режиме

    Symptom: a high-severity open Firebase rules finding. Cause: generated rules read allow read, write: if request.time < timestamp.date(2026, 6, 1); — a time-bounded "allow all". Fix: scope each rule to the authenticated user — match /users/{userId}/posts/{postId} { allow read, write: if request.auth.uid == userId; } — and re-deploy firebase deploy --only firestore:rules.

  3. Разрешительное CORS выживание в производстве

    Symptom: a high-severity CORS misconfiguration finding. Cause: generated Express middleware: app.use(cors({ origin: '*' })). Fix: allowlist your frontend origin: app.use(cors({ origin: ['https://your-app.com'], credentials: true })). For Next.js API routes, set Access-Control-Allow-Origin explicitly in the response.

  4. RLS включено, но не принудительно

    Symptom: FixVibe reports that anonymous visitors can read a public table even though RLS looks enabled in the dashboard. Cause: RLS is on, but a policy such as USING (true) lets the anon role through, or the migration that tightened it never ran in production. Fix: replace the permissive policy with one scoped to auth.uid(), apply the migration, and re-scan.

  5. Неподписанные IDOR-проходимые идентификаторы

    Symptom: an active scan on your verified domain reports that one user can read another user's records at /api/items/1, /api/items/2, ... Cause: the API handler trusts the path param and queries Postgres without an ownership predicate. Fix: add .eq('user_id', user.id) on every read query, or move to signed URLs / UUIDs scoped under /api/users/[uid]/items/[id].

Цикл безопасности с помощью vibe-кода

Целью не является идеальная безопасность; это устраняет низко висящие плоды, которые инструменты AI постоянно упускают из виду, поэтому вы можете продолжать доставку быстро.

  1. Generate fast — используйте Cursor, Claude Code, Lovable, Bolt. В этом вся суть.
  2. Audit immediately — запустите указанный выше набор grep, проверьте RLS, проверьте CSP, просмотрите границу аутентификации.
  3. Harden at deploy — промежуточное программное обеспечение, разделение среды, CSP nonce, HSTS, проверка подлинности только на сервере.
  4. Monitor — FixVibe пассивно ежедневно, активно еженедельно на проверенном домене, вебхуки на Slack, обнаружение угроз на Unlimited.
  5. Fix fast — use FixVibe coding-agent prompts for code/config findings and operator steps for DNS, provider, secret-rotation, or manual-review findings. Re-deploy, re-scan, close the loop.

Следующие шаги

Концептуальную основу по сравнению DAST и SAST и почему приложениям, созданным AI-, необходимо собственное сканирование, читайте в AI-generated code security scanning. Краткую справку по аудиту перед отправкой см. в vibe coding security checklist.

// scan your app

Хватит читать. Найдите бреши в своём приложении.

Drop in a URL — FixVibe runs every passive check from this guide plus the rest of its 230+ passive checks, usually in under a minute. Free, no install, no card.

  • Free уровень — 3 скана/месяц, без карты.
  • Пассивное сканирование любых URL — проверка домена не требуется.
  • Настроен на Cursor, Claude Code, Lovable, Bolt, v0, Replit.
  • Coding-agent prompts for code/config findings, plus operator steps for DNS/provider fixes.
Как защитить приложение, построенное с помощью AI-инструментов кодирования · FixVibe