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

Коротко
- Дві різні проблеми звуться однаково: відкритий файл .env. Ця про сам файл, що лежить на вашому вебсервері й доступний для завантаження кожному, хто набере адресу.
- Хостинг, який віддає лише ваш зібраний фронтенд, так не вміє. Справжній сервер уміє, і саме тому сім із восьми застосунків, які ми знайшли, були застосунками Replit.
- Із 30 761 живого застосунку, де наша перевірка отримала відповідь, 8 віддавали приватний файл. Дванадцять адрес скажуть, чи ви дев’ятий.
Хтось сказав вам, що ваш файл .env відкритий, і з цієї фрази неможливо
зрозуміти, наскільки це серйозно. Вона покриває дві цілковито різні ситуації.
Одна з них це інструмент, який працює так, як задумано. Друга означає, що файл,
який ви вважали приватним, можна завантажити з вашого живого сайту, будь-кому,
відколи він там лежить.
Ось та частина, яку інструкція за інструкцією змішує: значення з вашого .env,
що опинилося всередині вашого JavaScript, це не та сама подія, що сам файл
.env, який віддає ваш вебсервер. Перше це збірка, яка зробила те, про що ви
попросили, назвавши змінну VITE_ЩОСЬ. Друге це помилка зберігання, і саме її
варто перевірити першою, бо ви можете перевірити її самі ззовні приблизно за
хвилину.
Уявіть свій живий сайт як прилавок крамниці. Усе, що лежить на прилавку,
призначене, щоб його брали: сторінка, картинки, JavaScript, логотип. Підсобка за
ним тримає те, на чому крамниця тримається, і відвідувач не має туди жодної
дороги. Значення, вкомпільоване у ваш JavaScript, це рядок, надрукований у
буклеті на прилавку. Файл .env, який відповідає за публічною адресою, це тека
з підсобки, залишена на прилавку разом з усім іншим.
Чи відкритий мій файл .env?
Відкрийте свій живий сайт у браузері, додайте /.env у кінець адреси й
натисніть enter.
Повернутися може три речі, і проблемою є лише одна з них.
Сторінка вашого застосунку. Більшість фронтендів відповідає на кожну невідому адресу власною індексною сторінкою, бо саме так маршрутизує односторінковий застосунок. Ви отримуєте свою головну або екран «не знайдено». За цим шляхом нічого не віддається.
Помилка. 403 або 404 означає, що сервера запитали й він відмовив. Це та відповідь, якої ви хочете.
Звичайний текст із рядками. Щось на кшталт SUPABASE_URL=https://… і
SUPABASE_SERVICE_ROLE_KEY=eyJ…, моноширинною стіною без жодного оформлення.
Файл віддають кожному, хто попросить, і так відбувається з того дня, коли його
почали віддавати.
Дві різні речі звуться відкритим файлом .env
Одна про ваш бандл. Друга про ваш сервер.
| Що сталося | Де значення | Чого коштує лікування |
|---|---|---|
Ви назвали змінну VITE_, NEXT_PUBLIC_ або EXPO_PUBLIC_ | Вкомпільоване в JavaScript, який завантажує кожен відвідувач | Перенести роботу на сервер, потім замінити ключ. Файл ніколи не віддавався. |
| Ваш вебсервер публікує теку проєкту | У файлі, за https://вашсайт/.env | Забрати файл із того, що сервер публікує, потім замінити все, що в ньому було. |
Перший рядок це поширений випадок, і він має власну статтю: префікс це вказівка публікувати, і збірка її виконала. Усе далі тут про другий рядок.
Чому застосунок Lovable так не може, а Replit може
Бо ці два хостинги публікують різні речі.
Білдер, який деплоїть статичний фронтенд, передає хостингу теку зібраних файлів:
трохи HTML, трохи JavaScript, кілька картинок. Ваш .env прочитали під час
збірки, і в цій теці його немає, тож за /.env нічого брати. Хостинг не зміг би
віддати файл, навіть якби захотів.
Застосунок Replit зазвичай запускає власний сервер, у власній теці проєкту, з вашими файлами поряд із вашим кодом. Сервер, якому сказали віддавати його теку, віддає кожен файл у ній, і він ніяк не може знати, що в одному з них ваші ключі. Те саме стосується всього, що ви задеплоїли на власну машину.
Цифри йдуть за архітектурою. Із 30 761 живого застосунку, де ця перевірка отримала відповідь, 8 віддавали щонайменше один приватний файл. Сім із восьми були застосунками Replit, а застосунків Replit було 3 025 із 30 761. Восьмий стояв на власному домені, що каже те саме інакше: хтось тримав власний сервер.
Це найрідкісніша знахідка, яку ми друкуємо, і єдина з дев’яти без невинного пояснення. Якщо хочете ширший погляд на те, що насправді віддають застосунки Replit, ми просканували 3 042 з них.
Дванадцять шляхів для перевірки
Дванадцять адрес, у тому порядку, у якому їх варто набирати. Додайте кожну після свого домену.
| Адреса | Що в ній | Якщо відповідає текстом |
|---|---|---|
/.env | Кожен ключ, з яким збирали ваш застосунок | Вважайте все публічним і міняйте |
/.env.local | Те саме, з локального запуску | Те саме |
/.env.production | Те саме, з вашого живого деплою | Те саме |
/.git/config | Віддалена адреса вашого репозиторію | Уся директорія .git зазвичай читається |
/.git/HEAD | На якій ви гілці | Те саме, і це найтихіший із дванадцяти |
/.aws/credentials | Довготривалі ключі Amazon | Замініть в Amazon, потім перевірте рахунок |
/database.sql | Схема і рядки | Усі рядки всіх таблиць публічні |
/dump.sql | Те саме | Те саме |
/backup.sql | Те саме | Те саме |
/config.json | Те, що ви туди поклали | Прочитайте й подивіться; токени там трапляються частіше, ніж думають |
/docker-compose.yml | Описи сервісів, часто з паролями всередині | Замініть усе, що там написано |
/.npmrc | Токен реєстру | Відкличте токен |
Наше власне сканування читає всі дванадцять ззовні й називає ті, що відповіли. Воно триває близько 20 секунд і не потребує облікового запису: просканувати застосунок.
Що насправді означає влучання
Усе в цьому файлі публічне прямо зараз, і було таким з дня, коли його почали віддавати.
Ви не дізнаєтеся, хто його прочитав. Білдер не дає вам журналу завантажених файлів, у який можна зазирнути, а запит виглядає як будь-який інший запит на будь-який інший файл. У чому можна бути певним, так це в тому, що хтось пробував: автоматичні краулери обходять саме ці дванадцять шляхів по всьому інтернету, без упину, і їм не треба знати, хто ви, щоб знайти ваш.
П’ять із восьми застосунків, які ми знайшли, віддавали один із чотирьох одразу
дорогих: .env, директорію .git, .aws/credentials або дамп .sql. Решта
три віддавали config.json, docker-compose.yml чи .npmrc. Саме ці три
вважають нешкідливими, і саме тому в них опиняються токени й паролі до бази.
Чим усе це не є, так це вироком вашому застосунку. Зовнішня перевірка читає те, що ваш сайт віддає незнайомцю, і дванадцять шляхів, які відповідають нічим, це дванадцять шляхів, які відповідають нічим. Що ще витікає з vibe-coded застосунку це довший список.
Те, що псує тиждень: директорія .git
Директорія .git це не один файл. Це вся ваша історія.
Там кожен коміт, зокрема той, де ви вставили ключ, і пізніший, де ви його
прибрали. Саме це люди розуміють неправильно про витік .git: видалити секрет
із коду, який у вас сьогодні, не робить нічого з версією, де він ще був, а ця
версія лежить у тій самій директорії, яку публікує ваш сервер.
Перевірка читає /.git/config і /.git/HEAD, бо ці два малі, їхній вміст ні з
чим не сплутати, і відповідь будь-якого з них означає, що віддається сама
директорія. Далі читачеві не потрібен жоден особливий інструмент. Формат
задокументований, і звичайне програмне забезпечення його клонує.
Якщо ключ колись був у коміті, допоможе лише заміна. Що йде першим, залежить від того, чи копія вже назовні, і цей порядок варто взяти правильно.
Дамп, який хтось залишив у теці
Три з дванадцяти шляхів це дампи бази: /database.sql, /dump.sql і
/backup.sql.
Дамп це кожен рядок кожної таблиці в одному файлі, зі схемою зверху. Електронні адреси, хешовані паролі, замовлення, повідомлення, усе, що тримає ваш застосунок. Він відповідає за публічною адресою з найнуднішої причини в усій цій статті: хтось зробив відповідальну річ, зняв копію своєї бази й зберіг її в теці проєкту, у якій саме стояв.
Тож файл, зроблений, щоб захистити дані, став найшвидшим способом прочитати їх усі. Де живе копія, це така сама частина рішення, як і те, чи ви її робите. Три способи зробити резервну копію бази Supabase проходить варіанти, а Reeve Care тримає копію вашої бази Supabase поза вашим власним сервером, перевірену, перш ніж вона зарахується як резервна копія.
Як це виправити й чому порядок має значення
Заберіть файл із того, що публікує ваш сервер. Потім замініть кожен секрет, який у ньому був. Саме в такому порядку.
Міняти першим здається терміновою половиною, і це та половина, яка витрачає роботу намарно. Ваші нові ключі йдуть у той самий файл, файл далі відповідає за тією самою адресою, і ви замінили ключі просто назад у витік. Нічого не стало безпечнішим, ніж десять хвилин тому.
Куди його покласти натомість, залежно від того, що ви запускаєте:
- Хостинг із власним сховищем секретів. Replit Secrets, змінні середовища платформи, панель вашого хостингу. Значення читає ваш сервер під час роботи, і воно ніколи не лежить файлом усередині опублікованої теки.
- Поза текою, яку віддають. Якщо ви керуєте конфігурацією сервера, наведіть її на теку збірки, а не на корінь проєкту. Тоді файл у корені взагалі не має адреси.
- У репозиторії теж ні, що є окремою звичкою і доброю. Сьогоднішній проблемі вона нічим не зарадить.
Останній пункт варто сказати прямо, бо .gitignore це відповідь, по яку тягнуться
всі. Він тримає файл поза вашим репозиторієм. Файл на вашому сервері опинився
там, бо сервер сидить у вашій теці проєкту, і git не має щодо цього жодної думки.
Що зробити просто зараз
Що робити
- Наберіть усі дванадцять адрес після власного домену або запустіть безкоштовне сканування й дайте йому набрати. Читайте те, що повертається, а не код статусу.
- Якщо одна з них відповідає вашими власними налаштуваннями, заберіть файл із теки, яку публікує ваш сервер, і задеплойте знову. Це той крок, який це спиняє.
- Потім замініть кожен ключ, пароль і токен, які тримав файл, у кожного постачальника. Заміна до перенесення файлу кладе нові значення туди, де були старі.
- Після цього перегляньте рахунки та журнали постачальника. Заміна спиняє те, що буде далі, і нічого не робить із тим, що вже сталося.
- Якщо директорія
.gitчиталася, замініть усе, що колись було в коміті, а не лише те, що в коді сьогодні.
Якщо вам зручніше пройти застосунок списком, десятихвилинний чеклист безпеки покриває це разом з іншим, що варто закрити у щойно запущеному застосунку.
Поширені запитання
Як дізнатися, чи мій файл .env публічний?
Відкрийте свій живий сайт у браузері, додайте /.env у кінець адреси й натисніть enter. Якщо повернулася сторінка вашого застосунку, за цим шляхом нічого не віддається. Якщо повернувся звичайний текст із рядками на кшталт SUPABASE_URL=https://abcdefghij.supabase.co, файл віддають кожному, хто попросить. Зробіть те саме з /.env.local і /.env.production, бо сервер, який віддає один, зазвичай віддає всі три.
Файл .env у моєму бандлі це те саме?
Ні, і лікується інакше. Значення, яке опинилося всередині вашого JavaScript, потрапило туди, бо так попросили збірку: змінна звалася VITE_ або NEXT_PUBLIC_ або EXPO_PUBLIC_. Файл ніколи не залишав вашу машину, а значення залишило. Це окрема проблема з окремою статтею. Ця стаття про сам файл, який віддає ваш вебсервер, а отже кожне значення в ньому публічне, з префіксом чи без.
Чому хтось може завантажити мою теку .git?
Бо ваш сервер дивиться на теку проєкту, а .git це директорія всередині неї, як будь-яка інша. Сервер не знає, що одна з них ваша історія версій. Якщо /.git/config або /.git/HEAD відповідає текстом, уся директорія зазвичай читається, а це кожен коміт, який ви коли-небудь робили, а не лише код, який у вас сьогодні.
Я знайшов backup.sql на власному сайті, що тепер?
Заберіть його з теки, яку публікує ваш сервер, перш ніж робити будь-що інше, бо файл віддають просто зараз, поки ви це читаєте. Далі вважайте публічними кожен пароль, ключ і персональні дані в ньому: дамп містить схему і всі рядки всіх таблиць. Потім з’ясуйте, як він там опинився, зазвичай хтось зробив копію в теці проєкту й ніколи її не переніс.
Спершу змінювати ключі чи видаляти файл?
Спершу перенесіть файл, потім міняйте ключі. Зміна ключів, поки файл ще віддається, записує нові значення в те, що кожен може завантажити, тож ви витрачаєте роботу й опиняєтеся там само. Коли за цим шляхом уже ніщо не відповідає, замініть кожен секрет із файлу в кожного постачальника, а потім перегляньте рахунки та журнали.