Простое шифрование БД SQlite

    Так получилось, что я очень люблю использовать SQLite СУБД.


    Программируя на ассемблере, я иногда нуждаюсь в полноценной СУБД. Мои программы редко превышают в размере несколько сотен килобайт. Понятно, что использовать с ним СУБД в несколько сотен мегабайт по меньшей мере просто смешно, а в конце концов, очень неудобно – сразу возрастают требования к оборудованию и сложности установки и настройки, а в итоге уменьшается надежность всей системы.


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


    К примеру, я использовал SQLite в моем движке форума AsmBB о котором уже писал на Хабре. (Кстати, после этого он так и не упал).


    С того времени проект медленно, но уверенно развивается. Появились новые функции, повысились безопасность и быстродействие.


    И вот однажды я задумался как повысить и так неплохую безопасность проекта. И сразу подумал, что неплохо было бы сделать шифрование БД форума. Ведь если даже база и утечёт, то доступ к личным данным пользователей никто не получит.


    Быстрый поиск по Интернету показал, что есть несколько расширений SQLite для шифрования БД. К сожалению, официальное расширение SEE несвободно и вообще продается за деньги.


    Но, конечно, свято место пусто не бывает и я сразу наткнулся на расширение SQLeet. И в нем мне понравилось буквально все.


    SQLeet использует алгоритм ChaCha20 для шифрования БД. Ключ шифрования вычисляется через PBKDF2-HMAC-SHA256, используя 16-байтовую соль и 12345 итераций хеширования. Для аутентификации используется Poly1305.


    И SQLeet и SQLite распространяются на условиях общественного достояния (public domain). Это удобно, так как не увеличивает лицензионный хаос в проекте.


    Еще SQLeet очень компактный. Весь код занимает только около полутора тысяч строк на C и не имеет внешних зависимостей.


    Проект активно поддерживается и автор оперативно отвечает на вопросы и исправляет баги, если такие найдутся.


    SQLeet распространяется тем же самым образом, как и SQLite – в форме единственного исходного файла на C, который можно просто скомпилировать тем же самым образом как компилируется и SQLite.


    К тому же, так как расширение никак не меняет код SQLite, то обновления основной СУБД можно делать очень просто – через замены файла sqlite3.c и пересоздание объединенного исходника.


    Так как в AsmBB я использую не совсем стандартную компиляцию (SQLite в AsmBB компилируется через MUSL libc), а я не являюсь C программистом, то для меня простота компиляции очень важна.


    Вот, например, баш код, который я использую, чтобы скачать последнюю версию SQLeet и создать и скомпоновать исходник:


    wget -q -O - https://github.com/resilar/sqleet/archive/master.tar.gz | tar -xz 
    cd ./sqleet-master
    script/amalgamate.sh < ./sqleet.c > ../sqlite3.c
    cd ..
    rm -rf ./sqleet-master/

    В результате получается файл sqlite3.c который можно вставить там, где раньше вставлялся оригинальный файл SQLite и использовать тем же образом.


    Использование расширения тоже никак не отличается от использования SQLite. Разница только в том, что если БД зашифрована, то сразу после открытия надо вызвать функцию sqlite3_key(), в которой указать пароль шифрования. Ну или даже лучше, просто исполнить SQL pragma key='%пароль%'. (Это лучше потому что API интерфейс к SQLite не меняется и
    всегда можно заменить SQLeet на SQLite и обратно).


    Начальное шифрование БД, а также замена пароля, происходит через функцию sqlite3_rekey() или pragma командой pragma rekey='%NEW_PASSWORD%'.


    И вот здесь у меня возник такой вопрос. Откуда вообще брать пароль? Ведь если пароль сохраняется на сервере в каком-нибудь файле, то потенциальный хакер сможет прочесть его.


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


    А раз так, то пароль просто может ввести администратор через веб-интерфейс сразу после запуска AsmBB. Таким образом, пароль существует только в RAM и только во время исполнения POST-запроса во время запуска приложения. (Конечно, не надо забывать затирать нулями всю память, в которой пароль существовал во время исполнения POST-запроса.)


    Из заданного пароля SQLeet генерирует ключ шифрования через PBKDF2-HMAC-SHA256, и этот ключ тоже хранится только в RAM.


    Конечно, такое решение несовершенно. Ключ шифрования, наверное, можно найти в RAM памяти, во время исполнения AsmBB, если у атакующего есть права администратора.


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


    Есть, кстати, и грабли, на которые можно наступить, применяя SQLeet (или другие криптографические расширения SQLite). И я на них, конечно, наступил.


    Проблема в размере страницы БД. В версиях SQLite более ранних, чем 3.12.0 (март 2016 года) дефолтный размер страницы был 1024 байта. В v3.12.0 его сделали 4096 байта. Этот размер, пользователь БД может менять из соображений производительности, а размер страницы записан в самой БД.


    Но если БД зашифрована, то размер страницы нельзя прочесть, а для расшифровки этот размер нужен, потому что каждый блок зашифровывается отдельно.


    Поэтому, если зашифровать БД с нестандартным размером страницы (для SQLeet стандарт 4096 байт), потом расшифровать не удастся, даже если задавать правильный пароль.


    Исправляется это просто – прежде, чем задавать пароль через sqlite3_key() или pragma key='%пароль%', надо задать правильный размер страницы через pragma page_size=%размер%.


    Другая возможная проблема в том, что после шифровки ОС уже не сможет распознать, что файл является SQLite БД. Это (насколько я читал) иногда приводит к каким-то проблемам, в частности в iOS. Решение этой проблемы есть, просто не шифровать первые 32 байта файла, но в детали я не вдавался.


    И напоследок насчет производительности. SQLeet работает очень быстро. После шифрования я не заметил какого-либо замедления работы системы на фоне нормальных флуктуаций производительности VPS. Прецизионные измерения, возможно, покажут какое-нибудь замедление, но оно наверняка будет в пределах меньше чем 10% от скорости не зашифрованной БД.


    Конечно, есть и другие свободные расширения SQLite для шифрования. Например, SQLcipher. Мне оно не подошло потому что у него другая лицензия распространения (BSD), намного больше размер кода и имеются внешние зависимости.


    Но, с другой стороны, SQLcipher намного старше и поэтому (возможно) более стабильный. Кому-то может и пригодится.

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 4

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

        Так намного сложнее будет, а некоторые метаданные все-равно будут видны.

        +1
        А раз так, то пароль просто может ввести администратор через веб-интерфейс сразу после запуска AsmBB.

        На шифрование делал примерно также, но делал дополнительный сервис для раздачи остальным сервисам ключей шифрования. Плюсы — если основные сервисы упадут, то не нужно заново вводить пароль, только при запуске сервиса раздачи ключей.
          0

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

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

        Самое читаемое