Если вы захотели воспользоваться публичным ресурсом вроде GitHub или GoogleDrive для хранения своего репозитория, но при этом не готовы делиться со всем миром результатами своего труда, то вам поможет шифрование файлов в гит-репозитории. Это не сверхсекретная технология и на эту тему есть некоторое количество небольших статей в интернете (и даже на Хабре), но все они являются вариацией куска документации git посвященного атрибутам и тему совершенно не раскрывают. Кроме этого, в процессе использования git в этом режиме появляются ньюансы использования, которые не всегда легко понять и решить и которые я в этой статье постараюсь осветить.

Окружение


Изначально предполагается наличие шифрующего софта на компьютере. Естественный выбор использовать openssl. В линуксе он есть по умолчанию. В windows он идет в комплекте с mingw в инсталяции git для windows. Единственное о чем нам надо позаботиться под Windows, чтоб папка в которой openssl находится (например «C:\Program Files\Git\mingw64\bin\») оказалась в переменной среды PATH. Тогда нам не придется создавать дополнительные утилиты (как это рекомендуется в статьях про шифрование в гите) и вся конфигурация намного проще.

Настройка git


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

* filter=openssl diff=openssl
.git* !filter !diff
init.txt !filter !diff

Если вы не настолько параноидальны, можете заменить вилдкард на *.java или *.cpp по вкусу.
Зачем init.txt? Сделаем небольшую памятку для себя, чтобы в случае клонирования репозитория не приходилось судорожно искать инструкцию в интернете. Кроме того, её можно весьма успешно использовать как шеловский скрипт для инициализации криптования в свежесклонированной копии.

Теперь создадим упомянутый файл init.txt:

<code>#This is protected repository. To initialize it you need:
#
# git clone -n https://github.com/<your_project_name>
# git checkout tags/init
# Then execute in shell:
# . init.txt <password for repository>
[ -z "$1" ] && echo "Argument required: <password>" && return
git config filter.openssl.clean "openssl enc -base64 -aes-256-ecb -S 123456789 -k $1"
git config filter.openssl.smudge "openssl enc -d -base64 -aes-256-ecb -k $1"
git config diff.openssl.textconv "openssl enc -d -base64 -aes-256-ecb -k $1 2&>/dev/null || cat"
git checkout master</code>

Убедившись в том, что git и openssl доступны в путях мы запускаем файл как и показано в комментарии (Если вы находитесь в Windows это надо запускать из git-bash).

. init.txt my_repo_pass

Или же просто руками запускаем 3 команды git config из этого файла заменив $1 в строке на пароль для репозитория.

Теперь добавим это всё в репозитарий:

git add .
git commit -m "protection initialization"
git push
git tag init
git push --tags


Вуаля, у нас всё готово к работе. Теперь мы можем добавлять новые файлы и они будут автоматически криптоваться.

Интеграция с Intelij


Теперь самое интересное. Если вы создадите закриптованный java проект на github и начнете с ним работать вы очень скоро заметите, что гитовый плагин в IDE упорно не хочет сравнивать файлы с предыдущими версиями. Это происходит из-за того, что intelij подтягивает предыдущие версии файла из репозитория с помощью команды git show. А теперь внимание: команды git show и git format-patch не используют фильтры по-умолчанию. Для этого необходимо им указывать опцию --textconv. Заставить это делать сам гитовский плагин я не смог, поэтому я сделал маленький враппер для git и указал его в настройках. Он автоматически добавляет эту опцию для команду show.

git4idea.bat


@echo off
set ARGS=%*
"C:\Program Files\Git\bin\git.exe" %ARGS: show = show --textconv %


Вариант для линукса (к сожалению не проверялось):

#!/bin/bash
export ARGS="$*"
git "${ARGS/ show / show --textconv }"


Теперь, надеюсь, тема раскрыта.

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


  1. ekadesign
    14.10.2017 13:49

    Нюансы. Отредактируйте, пожалуйста



  1. Fedcomp
    14.10.2017 14:01

    Хм, как то даже не думал что у гита есть шифрование.


    1. Sly_tom_cat
      14.10.2017 20:53

      Так его и нет, но прикрутить — можно даже шифрование.


  1. arandomic
    14.10.2017 14:50

    1. Технический аспект.
    Отлично. У нас все зашифровано и любое малейшее изменение в файле ведет к полному изменению его зашифрованной копии.
    Теперь посмотрим как работают pack-и git'а.
    Они стараются хранить diff-ы файлов, чтобы предотвратить рост размера репозитория.
    Проведите эксперимент — создайте два репозитория. Один в зашифрованном виде, другой — в незашифрованном.
    Положите в них один и тот же файл размером в 50mb (для наглядности)
    Измените в файле один символ и сделайте коммит.
    Повторите это действие несколько раз. (На обоих репозиториях синхронно)
    Теперь сделайте команду git gc на обоих репозиториях и сравните размер.

    2. Моральный аспект:
    «Если вы захотели воспользоваться публичным ресурсом вроде GitHub или GoogleDrive для хранения своего репозитория, но при этом не готовы делиться со всем миром результатами своего труда, то вам поможет»… приватный репозиторий от Github или собственный git-сервер.
    Шифруя открытые репозитории, вы нарушаете свое соглашение с Github


    1. Sly_tom_cat
      14.10.2017 17:54

      Второй пункт почему то мне на ум пришел самым первым.

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


      1. ZyXI
        14.10.2017 19:54

        По моему мнению создание репозитория на github и хранение на нём шифрованных данных вполне логично:


        1. Автор почему?то привёл в пример именно шифрование кодовой базы. Но вы можете, к примеру, сделать на этой основе свой парольный менеджер и хранить так пароли, при этом храня и историю их изменения, не опасаясь что?то случайно удалить.


          Ещё так можно публично хранить все.файлы скопом — в том числе .ssh — в зависимости от того, к чему именно вы имеете доступ такое может быть вполне приемлемо.


        2. В моём первом предложении не случайно отсутствует слова «публичный» и «открытый». Шифрование репозитория просто значит «я не доверяю github, но не готов поддерживать свой сервер». Ничто не мешает взять и также зашифровать приватный репозиторий.


          Заметьте, автор написал «воспользоваться публичным ресурсом». А не «публичным репозиторием».



        1. Sly_tom_cat
          14.10.2017 20:45

          О, шикарно, вы случайно не адвокатом работаете?

          Шифрование кодовой базы в публичном репозитории Github-а, у вас чудесным образом превращается в шифрование паролей на всего лишь публичном ресурсе…

          Ну да, конечно, свои шифрованные пароли просто обязательно хранить в общедоступном месте, других мест — конечно же нет. :)

          И вот уж совсем интересный поворот — предложение шифровать приватный репозиторий… м-да… :/


          1. ZyXI
            14.10.2017 22:10

            А вы не политиком? Я вот не вижу, чтобы автор писал про публичный репозиторий, не покажете? Может ещё скажете, что там же упомянутый GoogleDrive стал внезапно публиковать файлы по?умолчанию? А в начале комментария про пароли я вообще написал «Автор почему?то привёл в пример именно шифрование кодовой базы» а не «Автор на самом деле имел ввиду шифрование паролей».


            Кроме того, где ваши предложения про «другие места»? Почему вам не нравится шифрование приватных репозиториев? В отличие от вас, я написал, какая может быть мотивация за этим действием.


            В общем ваш комментарий практически не содержит смысла, но зато содержит пачку ораторских приёмов. Политику бы определённо подошёл.


            1. Sly_tom_cat
              15.10.2017 00:14

              Да видимо адвокат. На одно возражение — ушат претензий и обвинений....


              1. Osykov Автор
                15.10.2017 22:15

                У вас такая живая беседа задалась, что даже не хочется встревать :)
                По первому комментарию — за использование шифрования приходится платить. Как и за всё хорошее в этом мире.
                По второму — едва ли хранение репозитория на гуглдрайве противоречит соглашению с гуглом. Зато ему полностью соответствует индексирование вашей почты и содержимого гуглдрайва гуглом. Я не желаю, чтоб мои файлы с паролями попадали в базы данных гугла.
                А по поводу гитхаба — я предлагаю вам указать на пункт соглашения, который мешает мне шифровать некоторые файлы репозитория. Я читал это соглашение — а вы? А по поводу необходимости — есть некоторые случаи (например связанные с правами на код) которые могут помешать выложить часть кода в чистом виде. Если у вас проект находится на границе лицензий и какая-то его часть, к примеру, не может быть GPL. А если у вас подобной необходимости никогда не было, то непонятно почему вы развили такой спор под этой статьей.


                1. Sly_tom_cat
                  15.10.2017 22:51

                  GPL как раз не позволяет скрывать код. Никакой. Ни свой ни встроенный.
                  Но если вы хотите скрыть весь код (а именно это предложено в статье) то зачем тогда его располагать на публичном ресурсе?
                  Про гуглодрайв — речи не было. В статье именно на гитхаб заливается шифрованные исходники. Потому и тред развернулся...


                  Кстати держать репы в облаках — не очень удобно. Яндекс например упорно стирает права на исполнение файла (под linux).


                  1. Osykov Автор
                    15.10.2017 23:04

                    Код распространяемый под GPL действительно не позволяет изменять предоставляемый код. Но что, если у вас в репозитории есть и какая-нибудь утилита, которую можно использовать но нельзя предоставлять в виде кода по условиям лицензии? А всё, что помещается на гитхаб автоматически соответствует его лицензии.
                    Как это «про гуглдрайв не было речи», когда он упомянут ещё в аннотации? Фактически изначально я шифрование поднимал именно на гуглдрайве а на гитхаб переехал значительно позже.
                    К слову про гуглдрайв — на днях выложу статейку про «родное» криптование документов в гуглдоксах. Работает и на телефоне.


                  1. ZyXI
                    15.10.2017 23:28

                    GPL позволяет скрывать весь свой код — пока вы не начали распространять программу, скрывайте сколько хотите. Можете сделать свой закрытый форк чего угодно с лицензией GPL, главное — не предоставляйте его клиентам, они могут потребовать от вас код, а вы не можете в ответ потребовать NDA: https://gcc.gnu.org/ml/gcc/2001-07/msg01342.html.


    1. deniskreshikhin
      14.10.2017 20:17

      У нас все зашифровано и любое малейшее изменение в файле ведет к полному изменению его зашифрованной копии.

      Если шифровать построчно, то такой проблемы нет.


      Моральный аспект

      На GitHub не обязательно хранить код. Можно зип-архивы, бинарки и т.д. Плюс это еще и статический хостинг сайтов.


      1. ZyXI
        14.10.2017 22:20

        Если шифровать построчно, то вам понадобится сравнительно сложный скрипт построчного (де)шифрование и понимание криптографии на достаточно высоком уровне, чтобы скрипт не сделал шифрование хуже, чем бесполезным: напоминаю, что если шифровать именно код, то там будет очень много весьма предсказуемых строк вроде «пустая строка», «строка с пробелами и закрывающей фигурной скобкой» и какие?нибудь #!/usr/bin/env bash. Одна ошибка в скрипте и эти строки выдают злоумышленнику пароль, всего лишь за возможное уменьшение размера репозитория.


        1. deniskreshikhin
          14.10.2017 22:31

          Да вполне тривиальная вещь на самом деле https://ru.wikipedia.org/wiki/Galois/Counter_Mode


          1. mayorovp
            15.10.2017 11:50

            Не пойдет: при вставке строки все счетчики поплывут.


            1. deniskreshikhin
              15.10.2017 13:21
              -1

              Вот пожалуйста, небольшой скриптик https://gist.github.com/kreshikhin/af92121bf2fb5770562b5ff2952fa048


              Берём текст qsort на Haskell:


                  quicksort [] = []
                  quicksort (x:xs) = quicksort small ++ (x : quicksort large)
                    where small = [y | y <- xs, y <= x]
                          large = [y | y <- xs, y > x]

              Меняем в третьей строчки y <= x на x >= y:


                  quicksort [] = []
                  quicksort (x:xs) = quicksort small ++ (x : quicksort large)
                    where small = [y | y <- xs, x >= y]
                          large = [y | y <- xs, y > x]

              Прогоняем через стандартный AES-GCM (https://www.npmjs.com/package/node-aes-gcm) и сравниваем результирующий base64 по чанкам в 16 байт:


              0 kZIMx/mCB6iNQFsd => kZIMx/mCB6iNQFsd
              1 WVHSXTgttVxia1ka => WVHSXTgttVxia1ka
              2 O61ub9b0SDdOhmpT => O61ub9b0SDdOhmpT
              3 4SbidSozTV7YAm5V => 4SbidSozTV7YAm5V
              4 w6qNv691PuD0VvrE => w6qNv691PuD0VvrE
              5 MbCg+/2AH+T00sCE => MbCg+/2AH+T00sCE
              6 0cqUOqVS/0F6uYaW => 0cqUOqVS/0F6uYaW
              7 puAtHwP3eDzjBIwE => puAtHwP3eDzjBIwE
              8 gnt8zMhOLJ5wMSef => gnt8zMhOLJ5wMSef
              9 ZhcNUbidyJhAHvBc => ZhcNUbidyJhAHvBc
              10 pPOKCjs67wX09t2R => pPKKCDs67gX09t2R changed!!!
              11 yBuEPwNZFotmDESy => yBuEPwNZFotmDESy
              12 p9P8uwIe8Rru84H0 => p9P8uwIe8Rru84H0
              13 BxSGUwFBZm+Nl8TJ => BxSGUwFBZm+Nl8TJ
              14 XE0= => XE0=
              

              Изменился только 10 чанк, остальные чанки — ни до, ни после не меняются.


              1. mayorovp
                15.10.2017 13:23
                -1

                Не пойдет: при вставке строки все счетчики поплывут.


      1. a5b
        14.10.2017 22:28

        В данном случае шифруется поблочно — режим ecb, сжатие не включено (openssl enc -d -base64 -aes-256-ecb -k $1; https://www.openssl.org/docs/man1.0.2/apps/enc.html) — https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29 — т.е. каждый блок данных в 128 бит (16 байт) для данного ключа однозначно отображается в какой-то другой 16-байтный блок (еще в начале зашифрованного файла 8 байт Salt и в конце файла добавляется 1 блок PKCS#7 padding). Замена 1 символа в середине файла приведет к изменению 1 блока; добавление символов в середине файла приведет к сдвигу последующих данных, но добавление 16 байтов в середине (или кратного количества) не изменит последующую зашифрованную часть файла. Из-за base64 и переноса строк каждые 64 символа (хотя в вики заявляют про 80 символов) в строке помещается 3 блока по 16 байтов, поэтому добавлять или удалять текст из середины файла лучше размерами, кратными 48 байтам.


        Режим ecb не самый безопасный — одинаковые блоки открытого текста в каждом файле всегда кодируются в такие же блоки шифротекста — см https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption "Why shouldn't I use ECB encryption? / You should use any authenticated encryption mode, such as GCM, EAX or OCB.) и классический bmp "The ECB Penguin" image.


        Да, зато получается "We are using AES-256 ECB mode as the encryption algorithm, as it turns out a deterministic encryption works best with git (we will explain later)." — https://gist.github.com/dstrctrng/3430823


        1. deniskreshikhin
          14.10.2017 22:37

          Ага, ну это довольно известная проблема ECB. Но для построчного шифрования этот режим и не нужен. Т.к. важно не то что бы одинаковые строки шифровались одинаково. А одинаковые строки в том же месте.


    1. a1888877
      15.10.2017 22:45

      1. По первому моменту шифрование это ещё и оверхейд на скорость работы. Но такова цена безопастности, шифрование не бывает бесплатным.
      2. Нужно понимать, что шифруется не всё, а указанные маски. Даже для opensource шифрование к примеру ключей подписи бинарников — весьма актуально. А хранить их в репозитарии со всем остальным удобно.


    1. bores
      15.10.2017 22:47

      Вопрос по пункту 1.
      Вы случайно не путаете git и mercurial?
      https://git-scm.com/book/ru/v2/Введение-Основы-Git


      1. arandomic
        16.10.2017 11:21

        Нет. Вы просто не дочитали.
        git-scm.com/book/ru/v1/Git-изнутри-Pack-файлы


    1. arandomic
      16.10.2017 14:08
      +1

      Так. UPD:
      — Оба пункта относятся к шифрованию по маске **/*, т.е. полному шифрованию репозитория пофайлово.

      — Как мне подсказывает KOLANICH в ToS Github нигде явно не зафиксировано, что в public репозиториях нельзя использовать обфурскацию/шифрование, так что стоит говорить лишь о fair use.
      Также travis ci напрямую советует использовать шифрование.

      Если, скажем, шифровать какие-либо ключи для travis ci — то оба мои возражения отпадают.
      Технический — т.к. незашифрованные ключи обладают теми же свойствами, что и зашифрованные — они меняются не познаково, а целиком.
      Моральный — т.к. кодом мы всё еще делимся, просто при этом защищаем наш pipeline от вандалов.


  1. askv
    14.10.2017 15:55

    image


    1. Dreyk
      14.10.2017 22:17

      в воображении опровержителя криптоманьяка, те кто хотят взломать ваши пароли, находятся рядом с вами


      1. askv
        14.10.2017 22:57

        Те, кому очень надо, найдут :)


        1. Dreyk
          14.10.2017 22:58

          не знаю, как криптоманьяки в общем, а я больше защищаюсь от "ковровых бомбежек", а не целевых ударов


        1. Osykov Автор
          15.10.2017 23:28

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


  1. fRoStBiT
    14.10.2017 20:30

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


    1. Sly_tom_cat
      14.10.2017 20:49

      Простите, но вы кажется не поняли.

      В git шифрования нет, но гит настолько гибкий инструмент, что небольшими усилиями в него можно хоть черта в ступе прикрутить. И автор прикрутил шифрование.

      Как по мне так это скрещивание бульдога с носорогом…


      1. ZyXI
        14.10.2017 21:53

        Ага, гибкий. Но даже аналога фаз из mercurial почему?то нет, не говоря уже о чём?то более фундаментальном вроде changeset evolution. Для чего?то простого можно обвешаться атрибутами и хуками, и это гораздо проще, чем написать дополнение для mercurial или даже делать шифрование на «фильтрах» (аналогично атрибутам, вот только настройких не хранятся в репозитории), но если вам, к примеру, захочется иметь возможность объявлять «этот коммит опубликован, его нельзя менять (в т.ч. удаляя из ветки)» и «этот коммит не опубликован, его можно менять», то вам понадобятся, во?первых, хуки, во?вторых, отдельный сервер синхронизации фаз: средств добавления метаданных в коммиты нет вообще, тем более средств добавления метаданных, которые не учавствуют в расчёте хэша и могут быть изменены позднее.


        Конкретно с фазами проблема частично решается запретом force-push в «публикующих» репозиториях, но это, во?первых, менее удобно для пользователя, а, во?вторых, поднятие проблемы, решаемой фазами, не является целью комментария вообще. Приведя данный пример, длительное время существовавший в виде дополнения я просто показываю, что дополнения в mercurial обеспечивают намного бо?льшую гибкость, чем позволяет архитектура git.


        1. Sly_tom_cat
          15.10.2017 00:16

          Простите, но казалось бы: а причём тут mercurial.....


          1. avost
            15.10.2017 10:52
            -1

            Видимо потому, что если копнуть чуть глубже данного чуть менее, чем малополезного примера, то оказывается, что "настолько гибкий инструмент" не настолько уж и гибкий, что даже меркуриал его кроет ;)


            1. Sly_tom_cat
              15.10.2017 12:16
              -1

              Ну а бык корову кроет… Это конечно тоже важный факт в свете темы топика :)


              1. avost
                17.10.2017 14:56

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


  1. timzi
    14.10.2017 21:04

    От слов «закриптовать», «криптование»



  1. Dreyk
    14.10.2017 22:20

    pass как раз так и делает для хранения паролей в гите


    1. arandomic
      16.10.2017 14:57

      Хм. Судя по всему он просто делает checkout из зашифрованного гита в dev/shm
      Вопрос: достаточно ли сделать rm -rf из dev/shm, чтобы гарантировать, что никто больше из этой области памяти не прочитает наши пароли?
      Беглый гуглинг не дал результатов.
      Просто всяческое crypto-по активно использует zeromemory и прочие гарантии удаления данных из памяти.
      Как с этим в dev/shm?

      С другой стороны — это супер идея, «я ее джва года ждал». Хранить под гитом пароли! Спасибо за наводку!


  1. Ensay
    15.10.2017 21:46

    В конце статьи про интеграцию с интеграцию с Intelij.
    А как это в Linux сделать?


    1. Osykov Автор
      15.10.2017 21:54

      аналогично — вам нужен шеловский скрипт который будет вызываться вместо оригинального гита. Примерно такой:
      #!/bin/bash

      export ARGS="$*"
      git "${ARGS/ show / show --textconv }"


  1. python273
    15.10.2017 22:25

    Keybase недавно запустили шифрование git:
    https://keybase.io/blog/encrypted-git-for-everyone


    You can have as many repositories as you want, but the total for your personal repositories can't exceed 100GB.

    :)
    What if we're living in a simulation?
    Keybase offers no guarantees against sophisticated side-channel attacks by higher-level entities.


    1. Osykov Автор
      15.10.2017 22:36

      Да, с неделю назад. Надо посмотреть насколько стабилен сервис. Пока у меня пельмени в холодильнике старше. Кроме того для этого используется их приложение, что добавляет звено в цепочку и уменьшает её надежность. Кроме этого гитхаб это в том числе и вики/сайт/трекинг задач и промо-возможности. От добра добра не ищут.