Перейти до вмісту

Чи безпечний ваш застосунок на v0?

Чи безпечно будувати на v0? Згенеровані компоненти не ризик. Ризик в обв'язці, яку ви додаєте навколо, і одна назва змінної вирішує майже все.

Vlad Tkachenko4 хв читання
Картка перевірки безпеки Reeve для застосунків на v0, з логотипом v0 на білій плитці.

Логотипи належать їхнім власникам і показані, щоб позначити сумісність.

Коротко

  • Застосунок на v0 зазвичай можна публікувати спокійно: згенеровані компоненти не ризик. Ризик в обв'язці, яку ви додаєте навколо.
  • Будь-яка змінна з назвою NEXT_PUBLIC_… потрапляє у JavaScript, який завантажують ваші відвідувачі. Для публічного ключа це правильно, для секретного ні.
  • v0 не налаштовує правила вашої бази даних. У Supabase таблиці, створені виконанням SQL, стартують без Row Level Security.

v0 дає вам компоненти: робочий інтерфейс, згенерований і покладений у ваш проєкт. Чого він не дає, так це обв'язки за ними: змінних середовища, бази даних, правил про те, кому що читати. Саме в цій прогалині проєкт на v0 зазвичай іде не так, і йде не так тихо.

Ось що гайд за гайдом подає неправильно: згенерований код не ризик. Ризик – це одна угода про іменування у вашому файлі середовища й налаштування бази даних, яке до v0 не має жодного стосунку.

Яка половина проєкту на v0 є публічною

Половина з інтерфейсом, цілком.

Уявіть крамницю. Компоненти, які пише v0, є вітриною: кожна сторінка, кожна форма, кожна кнопка, і вся ця вітрина вручається кожному відвідувачу, що відкриває ваш сайт. Обійти це неможливо: браузер не намалює сторінку, якої йому не надіслали. За нею стоїть склад, ваша база даних, окрема будівля з власним замком.

Особливість проєкту на v0 у тому, що ці дві половини ви складаєте самі. Компоненти приходять, нічого не знаючи про ваші ключі чи ваші таблиці, тож кожне рішення про те, що переходить зі складу на вітрину, ухвалюєте ви у власному проєкті, зазвичай в одному файлі, повному змінних середовища.

Що насправді означає NEXT_PUBLIC_

Означає «поклади це у браузер».

Next.js, під який генерує v0, вирішує, що отримають ваші відвідувачі, читаючи назву змінної. Усе, що зветься NEXT_PUBLIC_ЩОСЬ, вкомпільовується у JavaScript, який віддає ваш сайт. Усе без цього префікса лишається на сервері. Те саме правило існує під іншими назвами в інших інструментах (Vite використовує префікс VITE_ і прямо каже, що ці значення пакуються у ваш вихідний код під час складання), але ефект той самий: префікс публікує значення.

Для URL проєкту чи публічного ключа це саме те, що треба. Для секретного це саме те, чого не треба.

Обидва звуться API-ключами. Лише один із них будь-коли призначався для читання.

Пастка має впізнаваний вигляд. Щось в інтерфейсі не може прочитати значення, тож до нього додають префікс, щоб помилка зникла, і вона зникає, застосунок починає працювати. Якщо значенням був ключ Supabase sb_publishable_… або старіший anon, нічого поганого. Якщо це був sb_secret_… чи service_role, помилка була системою, яка казала вам, що цей код належить серверу, і перейменування опублікувало ключ, який ігнорує кожне встановлене вами правило. Розрізнити ці два можна за хвилину, і це варто зробити один раз для кожного ключа у файлі.

Що вирішує, чи можуть сторонні читати ваші дані

Правила вашої бази даних, а не ваші компоненти.

Ваш застосунок і сторонній стукають в одні й ті самі двері. Хто увійде, вирішує налаштування, а не екрани вашого застосунку.

Якщо ваші дані в Supabase, це налаштування зветься Row Level Security: перемикач для кожної таблиці, який рядок за рядком вирішує, кому що читати. Коли він вимкнений, ваш публічний ключ повертає всю таблицю кожному, хто попросить. Коли ввімкнений і політику написано, повертає лише те, що політика дозволяє.

Supabase вмикає її за замовчуванням для таблиць, створених у Table Editor панелі, і не вмикає для створених виконанням SQL. Тож таблиці, які ви народили клацанням, зазвичай захищені, а будь-яка таблиця, створена файлом міграції, можливо, ні. Згодом обидві виглядають однаково, і обидві працюють.

Є друга версія цього, яка ловить тих, хто зробив усе правильно: таблиця може мати увімкнену Row Level Security і все одно бути відкритою для всіх через політику на ній. Увімкнено й захищено є двома різними станами.

Як перевірити власний проєкт на v0 приблизно за десять хвилин

Прочитайте свій файл середовища рядок за рядком. Кожна змінна з префіксом NEXT_PUBLIC_ є опублікованою. Спитайте себе про кожну: чи спокійно було б мені надрукувати це на головній сторінці? Якщо ні, їй не місце під цим префіксом.

Замініть усе, чого там ніколи не мало бути. У панелі свого постачальника створіть новий ключ і відкличте старий. Прибрати рядок із коду двері не зачиняє: старе значення досі є в історії версій і в кешованих копіях сайту.

Перевірте правила своїх таблиць. У Supabase Authentication → Policies перелічує кожну таблицю й показує, чи ввімкнена Row Level Security. Усе вимкнене може прочитати будь-хто, у кого є адреса вашого проєкту.

А потім подивіться ззовні. Кроки вище кажуть, що налаштовано; вони не кажуть, до чого справді можна дістатися. Наше безкоштовне сканування читає ваш живий сайт так, як це зробив би відвідувач, і дає оцінку приблизно за 20 секунд, без акаунта: сканувати застосунок.

Що робити

  • NEXT_PUBLIC_ не формальність. Він вкомпільовує значення у файли, які завантажує кожен відвідувач.
  • Додаючи префікс, щоб заглушити помилку, зупиніться й подивіться, що це насправді за значення.
  • Публічному ключу місце у браузері. sb_secret_… і service_role не мають бути там ніколи.
  • v0 не налаштовує правила вашої бази даних. У Supabase таблиці, створені виконанням SQL, стартують без Row Level Security.
  • Таблиця з увімкненою Row Level Security усе одно може бути відкритою для всіх. Вирішує саме політика.

Відкрийте свій файл середовища й прочитайте рядки NEXT_PUBLIC_ уголос. Якщо десь у них лежить те, чого ви не надрукували б на головній, замініть це зараз, а далі 10-хвилинний чекліст безпеки охопить те, що ще варто вимкнути у щойно запущеному проєкті.

Що насправді може піти не так із застосунком на v0

Жоден із цих пунктів не є вашою провиною, це звичайні побічні ефекти швидкої генерації коду. Ось що варто перевірити:

  • Секретний ключ у клієнтському компоненті (пастка NEXT_PUBLIC_)

    v0 будує на Next.js, у якого є правило, на якому люди спотикаються: усе назване NEXT_PUBLIC_, і будь-який ключ, вписаний прямо в клієнтський компонент, надсилається у браузер, де його може прочитати будь-хто. Легко вставити API-ключ у згенерований компонент, не усвідомивши, що тепер він публічний. Деякі ключі й мають бути публічними; Reeve відрізняє їх від тих, які не мають, тож без хибних тривог.

  • Відкрита база даних (RLS у Supabase вимкнено)

    Якщо ваш застосунок на v0 зберігає дані (часто в Supabase), є перемикач Row Level Security (RLS, захист на рівні рядків), що вирішує, хто може читати чи змінювати кожен рядок. Якщо він вимкнений, ваші таблиці можуть бути відкриті будь-кому, хто знайде адресу. Це найпоширеніша серйозна проблема згенерованих застосунків, і вона лишається невидимою, доки не подивишся.

  • Опубліковані Source maps

    «Карта коду» (source map) розкриває оригінальний код вашого застосунку будь-кому, хто відкриє інструменти браузера. Це корисно під час розробки, але якщо вона потрапляє в продакшн, то дає стороннім читабельну копію того, як влаштований застосунок, і полегшує пошук усіх інших прогалин. Reeve перевіряє, чи не відкриті ваші карти.

  • Відкриті API-маршрути

    Застосунки на Next.js часто містять API-маршрути: невеликі ендпоінти, що роблять щось на кшталт читання чи запису даних. Якщо один із них згенеровано без перевірки доступу, він може відповідати будь-кому, хто його викличе. Reeve перевіряє, чи відповідають ваші ендпоінти стороннім, жодного разу не використовуючи їх, щоб щось змінити.

  • Відкритий файл .env або конфігурації

    Файл .env містить ключі проєкту. Час від часу він помилково публікується разом із задеплоєним застосунком, і якщо до нього можна дістатися, це пряма стежка до всього найчутливішого. Reeve перевіряє, чи не доступний ваш файл потихеньку.

  • Відсутні заголовки безпеки та відкритий обмін (CORS)

    Невеликі налаштування, що підказують браузерам, як захищати відвідувачів, і чи дозволено будь-якому сайту звертатися до даних вашого застосунку. Окремо це дрібниці; разом вони розширюють щілину. Reeve позначає те, чого бракує.

Що таке Reeve і чим він не є

Reeve є безкоштовною перевіркою ззовні в режимі читання, наче інспектор, який перевіряє двері, не заходячи всередину. Це швидко, і це ловить поширені помилки з великими наслідками. Це не повний аудит безпеки, і чиста оцінка не є гарантією: вона означає, що очевидні двері зачинені.

v0 і Vercel постійно підвищують якість того, що генерується й деплоїться, а якщо ви використовуєте Supabase, її власний радник позначає проблеми з базою даних у панелі. І те, і те допомагає. Що додає Reeve: ви працюєте у v0, а не читаєте згенерований код рядок за рядком, і ті інструменти говорять мовою розробників. Reeve дивиться на весь задеплоєний застосунок ззовні й розповідає знайдене простими словами. А якщо ви не хочете про це думати, ми можемо стежити за ним замість вас.

Хочете, щоб цим займалися, а не лише перевірили?

Reeve Care продовжує стежити за вашим застосунком, робить резервні копії даних і допомагає полагодити, коли щось ламається, щоб ви могли будувати далі, а не хвилюватися.

Дізнатися про Reeve Care

Поширені запитання

Чи готовий код v0 до продакшну і чи безпечний він?

v0 створює чистий сучасний код, але «виглядає готовим до продакшну» не те саме, що «перевірено». Безпека залежить від того, як налаштований застосунок: де живуть ключі, чи ввімкнені правила бази даних, чи захищені ендпоінти. Reeve перевіряє відкриті частини безкоштовно приблизно за 20 секунд.

Чи може застосунок на v0 розкрити мої API-ключі?

Так, якщо ключ опиняється в клієнтському компоненті або в налаштуванні NEXT_PUBLIC_, Next.js надсилає такі в браузер. Не кожен ключ є проблемою: деякі й мають бути публічними. Reeve знаходить ключі у завантаженому коді й підказує, які безпечні, а які треба перенести на сервер.

Чи безпечні API-маршрути v0?

Вони можуть бути безпечними, але згенерований ендпоінт іноді виходить у реліз без перевірки автентифікації, а це означає, що будь-хто, хто його знайде, може його викликати. Reeve перевіряє, чи відповідають ваші ендпоінти стороннім; він лише перевіряє, що двері відчиняються, і ніколи не заходить усередину.

Чи зламає сканування мій застосунок на v0?

Ні. Reeve дивиться лише на те, що вже публічне ззовні. Він ніколи не входить в акаунти, нічого не змінює й не завантажує даних. Режим читання, наче перевірка, чи замкнені двері, не заходячи всередину.

Що насправді робить префікс NEXT_PUBLIC_?

Він каже Next.js вкомпілювати це значення у JavaScript, який іде до браузера. Значення перестає бути серверним налаштуванням і стає частиною ваших опублікованих файлів, доступною для читання будь-якому відвідувачу. Для публічного ключа це правильно, для секретного ні, і саме префікс є єдиним, що це вирішує.

Я перейменував змінну й додав NEXT_PUBLIC_, бо застосунок не міг її прочитати. Це була помилка?

Усе залежить від того, що в цій змінній. Якщо це був публічний ключ або URL проєкту, перейменування було саме тим, що треба. Якщо це був секретний ключ, застосунок не міг його прочитати, бо той ніколи не мав виконуватися у браузері, і перейменування його опублікувало. Замініть цей ключ, а код, якому він був потрібен, перенесіть у серверний маршрут.

Чи налаштовує v0 правила моєї бази даних за мене?

Сам собою ні. v0 генерує код інтерфейсу; базу даних і її правила ви налаштовуєте самі там, де їх хостите. Якщо ви на Supabase, Row Level Security увімкнена за замовчуванням лише для таблиць, створених у Table Editor панелі, і вимкнена для створених виконанням SQL.

Автор

Vlad Tkachenko

Засновник Reeve

Я щодня дивлюся на застосунки, зібрані в Lovable, Bolt, v0, Cursor і Replit, і на короткий список помилок, які трапляються в них знову і знову.

Більше про автора

Будували деінде? У нас є такий самий чесний розбір для:

← Усі гайди з безпеки конструкторів

Автоматична зовнішня перевірка, а не повний аудит. Відсутність знахідок не є гарантією безпеки.