Сразу про название. .fsecurity — файловое обозначение FlautSecurity, отдельной части инфраструктуры FlautCompany. Дальше в статье я буду использовать именно короткое название .fsecurity.

Идея появилась из довольно простой проблемы: если приложение хранит зашифрованные файлы, где-то должны находиться ключи. Часто их кладут в БД, конфигурацию или секрет-хранилище. Я решил построить архитектуру так, чтобы база данных вообще не содержала ключей, а .fsecurity стал отдельным криптографическим слоем внутри проекта.

При этом .fsecurity — не сервер, не прокси и не отдельный процесс. Это Python-модуль без собственного IP, порта и внешнего API.

Как работает .fsecurity

Для тестирования технологии я создал Telegram-бота @flautcloud_bot — облачное хранилище, на котором проверяю систему в реальных условиях.

Схема загрузки выглядит так:

Telegram   
  ↓
Application   
  ↓
.fsecurity   
  ↓
AES-256-GCM внутри .fsecurity   
  ↓
Зашифрованный файл   
  ↓
Storage

Пользователь отправляет файл, .fsecurity генерирует для него уникальный криптографический ключ и шифрует данные с помощью AES-256-GCM.

Для каждого файла используется отдельный ключ:

file A → K1
file B → K2
file C → K3

Ключи не вычисляются из user_id, file_id, имени файла или времени. Поэтому нельзя просто написать:

key = sha256(str(user_id).encode())

и получить ключ пользователя.

Ключ должен быть независимым криптографическим секретом.

В базе остаются только метаданные:

file_id
user_id
filename
storage_id
size
created_at

А encryption_key там отсутствует.

Что происходит с файлами

Здесь я решил добавить ещё один уровень защиты.

Зашифрованные файлы не должны находиться рядом с основной БД и обычной файловой системой приложения.

Storage должен быть отдельным закрытым хранилищем. Каждому файлу назначается случайный непрозрачный storage_id:

file_id: 18421
storage_id: 9c7f2a81d04e...

Сам файл может выглядеть примерно так:

9c7f2a81d04e....bin

Никаких:

/users/ivan/passport.pdf

или:

/928374/photo.jpg

Злоумышленник не должен получать возможность угадать расположение файлов по имени, user_id или последовательному ID.

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

Получается своеобразное разделение:

        Database           │       метаданные           │           ×           │      .fsecurity           │         ключи           │           ×           │        Storage      ciphertext

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

А если украли .fsecurity?

Это уже серьёзный сценарий.

Допустим, злоумышленник получил .fsecurity и Database.

При этом самих файлов у него всё ещё нет.

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

Теперь предположим обратное: злоумышленник получил Storage.

Перед ним будет примерно такой набор:

9c7f2a81d04e....bin
18af71c9b2e1....bin
7d441ac82f03....bin

Это шифротекст, а не исходные документы.

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

Ключи для файлов независимы друг от друга и не вычисляются из user_id, file_id, имени файла или других публичных параметров.

То есть нельзя просто взять:

user_id = 928374
file_id = 18421

и математически получить ключ от нужного файла.

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

Именно здесь работает идея разделения доверия: один компонент сам по себе не должен давать возможность восстановить пользовательский файл.

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

Почему файлы должны иметь случайные идентификаторы

Есть ещё одна небольшая, но важная деталь.

Не стоит хранить файл как:

/users/12345/photo.jpg

Если злоумышленник получил доступ к файловой системе, такая структура сама рассказывает ему слишком много.

Гораздо интереснее:

/storage/9c/9c7f2a81d04e8a....bin

Причём storage_id должен быть криптографически случайным и не иметь связи с user_id или последовательным file_id.

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

Это не заменяет шифрование, но добавляет ещё один слой защиты: затрудняется обнаружение и сопоставление объектов.

Можно усилить ещё больше

Для инфраструктуры с особенно высокими требованиями я бы добавил внешний KMS или HSM.

Тогда критический ключевой материал можно вынести за пределы самого сервера:

.fsecurity    
  ↓   
KMS/HSM     
  ↓
ключевой материал     
  ↓
AES-256-GCM     
  ↓
шифротекст

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

Это уже более серьёзная архитектура, требующая дополнительной инфраструктуры, политик доступа и резервирования.

Но базовая идея .fsecurity остаётся прежней: ключи не должны лежать в БД рядом с пользовательскими данными.

Почему AES-256-GCM?

Здесь я сознательно не изобретаю собственную криптографию.

Для шифрования используется AES-256-GCM — проверенный криптографический примитив, который обеспечивает конфиденциальность и проверку целостности.

Если кто-то изменит зашифрованный файл, проверка должна завершиться ошибкой.

Это принципиальный момент. Собственную архитектуру безопасности придумать можно. Собственный алгоритм шифрования — лучше не надо.

Иначе через пару лет кто-нибудь найдёт в коде функцию:

my_super_secure_encrypt()

и спросит, почему она состоит из XOR и трёх строк Python.

Что происходит при скачивании?

Обратная операция выглядит примерно так:

Пользователь     
  ↓
Telegram     
  ↓
Application     
  ↓
.fsecurity     
  ↓
получение нужного ключа     
  ↓
проверка целостности     
  ↓
AES-256-GCM decrypt     
  ↓
файл     
  ↓
Telegram     
  ↓
Пользователь

Основному приложению не требуется самостоятельно хранить или обрабатывать ключ.

Оно передаёт операцию .fsecurity, а криптографическая логика остаётся внутри этого модуля.

Именно поэтому .fsecurity можно воспринимать как отдельную границу между обычной логикой приложения и криптографией.

Почему .fsecurity — не отдельный сервер?

Можно было построить архитектуру так:

Application      
  ↓
Crypto Server      
  ↓
Storage

Но тогда появляется ещё один сетевой сервис.

А значит, появляются: отдельный порт, API, авторизация, TLS; firewall, сетевые ACL, мониторинг, дополнительная поверхность атаки, ещё один компонент, который нужно обновлять и защищать.

Мне хотелось сделать иначе:

Application      
  ↓
.fsecurity      
  ↓
Storage

Если компоненту не нужен сетевой интерфейс, зачем его создавать?

Поэтому .fsecurity — именно модуль внутри проекта, а не отдельный сервер.

Но это не «невзламываемая технология»

Здесь я специально не хочу использовать формулировку «невозможно взломать».

Так не бывает.

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

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

Его задача намного конкретнее:

Один украденный компонент не должен автоматически превращаться в украденные пользовательские файлы.

Если украли только БД — ключей там нет.

Если украли только Storage — там шифротекст.

Если получили .fsecurity без доступа к хранилищу — самих пользовательских файлов нет.

Если всё это находится в разных изолированных компонентах, атакующему приходится компрометировать сразу несколько уровней системы.

Именно это и есть основная идея архитектуры.

Зачем вообще всё это?

Для меня .fsecurity — не просто файл с функцией шифрования.

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

Бот нужен именно как испытательный стенд. Главная цель проекта — сама технология .fsecurity.

В дальнейшем .fsecurity планируется отделить от конкретного Telegram-бота и сделать самостоятельным решением, которое можно будет подключать к другим проектам.