Барев дзес! На связи Гагик Антонян. Я — Frontend-разработчик и это моя первая статья на Хабре. Сегодня вы узнаете, как полноценно удалять .env, который по ошибке попал на удалённый Github-репозиторий. Буду рад, если поддержите данный материал лайками и комментариями. А теперь погнали рвать пределы IT-галактики.

Разрабатывая приложения, часто сталкиваюсь с тем, как после очередного коммита, в репозитории вижу один из ключевых файлов, когда я работаю с переменными окружениями, оказалась на странице репозитория Github. Речь идет о файле .env, чей общедоступность может быть опасным. И для того, чтобы обезопасить хранение конфигурационных переменных и настроек приложения, используется данный текстовый файл.

Я работаю на VS Code, и, если честно, так и не понял, с какой стати .gitignore "не игнорирует" .env. Причем спокойно "игнорирует" другие файлы, директории.

Всё же, нужно действовать, исходя из конкретного кейса, но если вы не хотите, чтобы какой-нибудь John Doe воспользовался данными из вашего .env, то вы перешли по верной ссылке. Вы же не отдаете ключи грабителю с фразой "Грабьте мой дом", верно? Так же и тут. Поэтому предлагаю потихоньку начать.


Шаг 1: Создание проекта

В этом шаге я создам приложение Next.js. С этим проектом буду работать на VS Code. Осуществляю создание Next.js-проекта через пакетный менеджер pnpm.

pnpm dlx create-next-app@latest kill-env

После того, как я задал настройки в терминале, как будет выглядеть проект (в статье это не важно), перейдем к коду. Но перед этим хочу связать проект с удаленным репозиторием Github. Итоговую файловую систему можно увидеть на картинке.

Готовый проект в VS Code
Готовый проект в VS Code

Шаг 2: Подключение локального проекта с Github-репозиторием.

Ничего сверхъестественного, здесь создаю репозиторий для моего проекта и делаю последующее связывание с локальным репозиторием. На этой короткой ноте переходим к 3 шагу.

P.S.: Я не буду здесь прописывать и так ясные команды для связывания, поэтому результат представлен на картинке.

Репозиторий, откуда могут быть "сюрпризы"
Репозиторий, откуда могут быть "сюрпризы"

Шаг 3: Разбор кейса и его решение

3.1 В какую проблему можно попасть из-за невнимательности

Предположим, что я, увлекшись разработкой чудо-приложения, забыл к коммиту добавить файл .env в .gitignore, находящийся в корневой директории проекта. Из-за этого секретный файл попадает на страницу удалённого репозитория, что не хорошо.

Для приличия моего коммита я практически полностью удалил код разметки в одном из файлов моего проекта. Сюр в том, что коммит уже на удалённом репозитории и .env файл вместе с остальными изменениями. Как не должно быть в вашем репозитории показан на картинке:

Мои "поздравления", секретный файл теперь уже не секретный...
Мои "поздравления", секретный файл теперь уже не секретный...

Да, .gitignore файл не отменяет уже зафиксированные изменения (поэтому название папки зависимостей проекта закрашен в серый цвет но не несчастный .env. Это можно увидеть на приложенной картинке. И так, как мы можем это исправить?

Да, на это очень больно смотреть...
Да, на это очень больно смотреть...

3.2 Решение проблемы

Для начала воспользуемся следующей командой:

git rm -r --cached .env
Последствие выполнения команды git rm -r --cached .env
Последствие выполнения команды git rm -r --cached .env

Не бойтесь, мы удаляем файл .env и все его подпапки из области подготовки Git, но не из самой файловой системы проекта. Внеся изменения, увидим, что .env больше не числится в списках файлов проекта.

А где? где ты, .env?

Если вы думаете, что это happy end, то спешу разочаровать: это ещё не конец. Перейдя на историю коммитов можно обнаружить заманчивую вещь: хоть мы удалили файл из корневой директории проекта, данные из файла .env как были в истории, так и остались. Не верите? Показываю это на картинке:

Просчитался, но где, ара?
Просчитался, но где, ара?

Пришло время раз и навсегда расправиться с этой проблемой. Вводим следующую команду, которая удаляет все файлы и папки с именем .env из истории коммитов текущей ветки (HEAD):

git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch .env" HEAD

В ответ получим предупреждения о том, что это испортило нашу историю, поскольку это пройдет через всю нашу историю и на 100% устранит её существование. Выполняем следующую команду.

git push --force

После этой команды можно увидеть наши коммиты, однако содержимое файла .env больше не существует в истории коммитах. Показываю на пикче:

Миссия выполнена
Миссия выполнена

С помощью нескольких команд можно добиться такого результата: и .env удалён из удалённого репозитория, и не отображается его содержимое в истории коммитов. Надеюсь, что эта статья поможет вам более эффективно отправить на вечный покой файл .env.

По этой ссылке можно перейти на Notion-статью в укороченной версией с картинками и кодом

Спасибо, что вы прочли эту статью! До скорых встреч!

Комментарии (59)


  1. turboslon
    08.06.2024 15:45
    +38

    Если с репозиторием работают другие люди, переписывать историю может быть плохой идеей. Тогда можно просто поменять пароли/ключи, которые были упомянуты в файле, и добавить сам файл в .gitignore


  1. fshp
    08.06.2024 15:45
    +10

    А из истории пушей кто удалять будет?


  1. Mausglov
    08.06.2024 15:45
    +14

    Я работаю на VS Code, и я, to be honest, так и не понял, с какой стати .gitignore "не игнорирует" .env. Причем спокойно "игнорирует" другие файлы, директории.

    А разгадка проста: это не .gitignore игнорирует "другие файлы, директории" сразу "из коробки", а VS Code. В IDE от JetBrains то же самое.
    А .gitignore - просто текстовый файл. Он плюс .git/info/exclude плюс глобальный .gitignore - вот как определяется, будет проигнорирован файл/каталог или нет, если работать не через IDE.
    (Надеюсь, ничего не пропустил?)


  1. ajijiadduh
    08.06.2024 15:45

    из статьи понятно что вы не русский, но неужели нельзя было проверить текст с помощью русского человека или каким-нибудь сервисом?


    1. Absurd
      08.06.2024 15:45
      +4

      господи, как вас еще земля таких носит


    1. georgiy08
      08.06.2024 15:45

      Что конкретно вас смутило?


      1. GBR-613
        08.06.2024 15:45
        +2

        Например, не "чей общедоступность", а "чья...".


      1. ajijiadduh
        08.06.2024 15:45
        +4

        неправильное построение предложений например


    1. karavan_750
      08.06.2024 15:45
      +1

      из статьи понятно что вы не русский

      А вы?


    1. funca
      08.06.2024 15:45
      +3

      Из вашего комментария тоже понятно, что вы не лингвист. Хотите помочь автору исправить ошибки - есть соответствующая кнопка. Не хотите - не позорьтесь.


      1. ajijiadduh
        08.06.2024 15:45

        как вы это поняли и в чём позор?


        1. radioxoma
          08.06.2024 15:45
          +3

          В раздельном написании прилагательного https://ru.wiktionary.org/wiki/нерусский в отсутствие противопоставления.

          Преисполниться можно здесь.


          1. keystore
            08.06.2024 15:45
            +2

            Теперь он будет правильно писать эту фразу с новыми силами


          1. ednersky
            08.06.2024 15:45
            +1

            вообще капец: нерусский — прилагательное

            неамериканец — существительное

            русский — существительное

            и как в этом мире люди живут?


          1. ajijiadduh
            08.06.2024 15:45

            спасибо, запомню


    1. FurySeer
      08.06.2024 15:45
      +4

      фу таким быть


    1. nukhtarov
      08.06.2024 15:45

      Человек выучил твой язык, а ты ведёшь себя как *, вместо того, чтобы похвалить


  1. kbnrjlvfrfrfrf
    08.06.2024 15:45
    +9

    Гит ничего не удаляет. Можно пересоздать цепочку чтобы обойти коммит со злосчастным файлом, но сам этот коммит с этим файлом остаётся в этом "блокчейне" навсегда. Сможет ли кто до него добраться не зная хеша - другой вопрос. Ещё раньше можно было писать в суппорт гитхаба чтобы они окончательно удалили коммит. Как сейчас не знаю.


    1. petro_64
      08.06.2024 15:45
      +5

      Смотря какой гит имеется ввиду: в самом гите он не остаётся навсегда, а до первого вызова сборщика мусора, который удалит коммиты, которые не имеют референсов - если говорить про "обычный" гит. Онлайн сервисы работают по-своему, например Github сборщик мусора не вызывает. Но тем не менее, чекаут с таким коммитом сделать будет невозможно.


      1. kbnrjlvfrfrfrf
        08.06.2024 15:45
        +1

        Это я и имел ввиду. Свой гит - как бы может особо париться и не надо. А всё это [публично-]облачное баловство - хакнут-сольют, мало ли.


  1. Bumeranghc
    08.06.2024 15:45
    +7

    Интересное решение. Я знаком ещё с одним: BFG repo cleaner.


    1. wl2776
      08.06.2024 15:45
      +1

      BFG быстрее и умеет ещё кое-что, например, большие файлы удалять


      1. Fil
        08.06.2024 15:45
        +1

        BFG может даже разнести весь сервак на куски.
        P.S. Оказывается, название BFG repo-cleaner как раз в честь оружия, а не какая-то хитрая аббревиатура


  1. insecto
    08.06.2024 15:45
    +33

    Фиг с ним с gitignore. Зачем вы вообще изначально добавили .env в staging area перед коммитом? Или вы делали git commit -a? Это плохая привычка, надо избавляться. Или вы ничего не знаете про staging area и всё отдаёте на откуп IDE?

    Вообще, конечно, правильный ответ это не переписывать историю (дичь!), а ротировать утёкшие ключи. Как их не чисти, а ключи уже фактически утёкшие.


    1. Finesse
      08.06.2024 15:45
      +2

      Понятно, что ошибку проще не совершать, чем исправить. Но это не значит, что инструкция по исправлению ошибки не нужна.


      1. shaggyone
        08.06.2024 15:45
        +3

        Про мне автор дал не инструкцию по исправлению ошибки, а инструкцию по маскировке ошибки. Единственный способ что-то сделать это посыпав голову пеплом сообщить ответственному человеку, и уже он перегенерит все скопированные секреты.


        1. GospodinKolhoznik
          08.06.2024 15:45
          +1

          Подход "Старший придёт он разберётся", конечно имеет право на жизнь, но иногда надо брать ответственность и на себя.

          А если я в собственном проекте слил .env файл в гит, то кому "ответственному" мне сообщать?


          1. shaggyone
            08.06.2024 15:45
            +4

            Знаете, я четверть века назад проходил производственную практику на заводе, токарем. Там висели ещё советские плакаты с текстом: "Рабочий, не скрывай брак, покажи мастеру".

            В вашем случае вам нужно перегенерить секреты. Потом можете, и описанное в статье сделать.


            1. GospodinKolhoznik
              08.06.2024 15:45
              +1

              При чем тут сокрытие брака? И что делать если вы сам мастер? Разве нельзя самостоятельно исправить собственную ошибку и сказать команде - ребята, я там ошибся, поэтому все переделал, так-то и так хакнул репозиторий и перегенерил все ключи и пароли, например?


              1. ednersky
                08.06.2024 15:45
                +5

                если вы сделали git push, НИКАКИЕ манипуляции на вашей стороне, в том числе push —force не помогут очистить историю на стороне удаленной

                зная номер коммита этот файл всё равно можно достать

                потому правильную аналогию привели — это попытка сокрытия брака


              1. aamonster
                08.06.2024 15:45

                Это не исправление, вот в чём штука. Исправление – считать ключ утекшим (даже если сделать описанное в статье и сразу умудриться прогнать git gc на сервере – нет никаких гарантий, что кто-то не успел сделать pull) и перегенерить.
                Если прав на перегенерацию нет – ну, можно использовать, как временное решение, пока это не сделает тот, кто отвечает за ключи (но в этотвремя находитесь под угрозой).

                Ну и вообще git push --force – такое себе, создаёт на ровном месте геморрой тем, кто успел сделать git push, так что если есть возможность – лучше обойтись без этой игры в прятки.


              1. shaggyone
                08.06.2024 15:45

                Всё же ваш план работы включает перегенерацию ключей. Если включает, то ок. Хотя непонятно, зачем ритуальное действие с удалением .env файла из репозитория.

                В контексте нашей с вами беседы, у меня впечатление, что изначально перегенерация не предполагалась, то то что вы не пишете про взятие ответственности это декларация, вроде наклеивания шильдиков о том, что производство местное. Так делать не надо.


        1. vitaly_il1
          08.06.2024 15:45
          +1

          если репо публичное - да.
          а если нет - ИМХО, вполне нормально просто удалить.


          1. shaggyone
            08.06.2024 15:45

            Есть варианты.

            Пока пишем такую статью мы не должны:
            1. считать что гит репозиторий живёт где-то на кошернохаляльном гихтабе или у на аналогичном сервисе. Наример вполне возможен вариант, что читатель воспользовался воспользовался статьёй 15 летней свежести с хабра же, (статья называлась "From Git to Deploy", почему то не гуглится, но есть сотни подобных) и хостит какой то гит у себя, возможно просто пушит в папочку доступную по ssh. Возможно репозиторий ещё с тех времён живёт.
            2. Или кто-то из коллег читателя делает бэкап репозитория, bare репозитория, просто на всякий случай. Возможно даже читатель сам 100 лет назад такое организовал, и забыл об этом.
            3. возможно сервис корпоративный, который сопровождает корпоративный же девопс, возможно это "девопс после курсов" который знает как квалификационные тесты на этих курсах пройти, умеет проходить собеседования, и даже знает чем отличается симлинк от хардлинка. Но реальную задачу с которой не сталкивался на практике не сделает, условно, вроде такого, что сервис через systemd не сможет запустить, даже если ему пример дать и совместно с ним пару раз этот пример разобрать. И репозиторий декларированный как скрытый на деле -- доступен любому достаточно сильно того желающему.
            4. или окажется что сервис которым пользуетесь -- на деле решето.
            5. моя личная паранойя в том, что мы на слово верим тому же MS, что их админы не мониторят репозитории на предмет ключей доступа и ещё каких нибудь вкусностей.

            На мой взгляд, основной вопрос, это "что будет, если данные утекут". Если хакер упрёт рецепты с сайта до даты их запланированной публикации, наверное не страшно. Если данные там более ценные, я бы не рисковал. На деле, время неспокойное, и условный хакер может развлечься и разместить на сайте с рецептами материал, по поводу которого в одном регионе будут вопросы у властей, или в другом у "прогрессивной общественности". В любом случае, редактирование истории -- сомнительная практика, а в случае компрометации ключей -- это заметание мусора под ковёр.


    1. MadeByFather
      08.06.2024 15:45

      Или вы делали git commit -a? Это плохая привычка, надо избавляться.

      Чем? Проще все добавить и убрать что-то не нужное, посмотрев все файлы, чем каждый файл кликать, чтобы добавить в коммит, потому что добавляется как правило 90-95% файлов, так что работы в 10 раз меньше, чем идти от обратного.


      1. wl2776
        08.06.2024 15:45

        добавляется как правило 90-95% файлов

        От инструмента зависит. Если CMake + in source build, то она много файлов генерирует.


      1. aamonster
        08.06.2024 15:45

        Без разницы, добавляете вы в индекс файлы по одному или убираете, всё равно вы просматриваете их (а вот git commit -a наводит на мысль, что коммиты слишком жирные, раз их не просматривают)