Безопасная загрузка файлов: лимиты, права, URL и проверка
Безопасная загрузка файлов: права доступа, лимиты размера, подписанные URL и базовые схемы антивирусной проверки без лишней сложности.

В чем проблема с загрузкой файлов
Загрузка файлов часто становится первым инцидентом, потому что она пересекает границу между пользователем и сервером. Пользователь приносит "свой" контент, а приложение по привычке начинает ему доверять: сохраняет, индексирует, показывает другим людям, иногда даже обрабатывает (превью, распаковка, конвертация). Этого достаточно, чтобы маленькая удобная фича превратилась в дыру.
Потери обычно выглядят прозаично. Утечки случаются, когда файлы лежат в общем бакете или отдаются по угадываемому пути. XSS появляется, когда вы показываете загруженный HTML или SVG как страницу, или отдаете файл с неправильным Content-Type, и браузер начинает исполнять содержимое. Переполнение диска и счета за хранилище прилетают, когда нет лимитов размера, квот и контроля количества загрузок. Отдельный риск - вредоносные вложения: их могут разослать через ваш сервис другим пользователям, а иногда и попытаться заставить бэкенд "проглотить" опасный формат.
"Безопасная загрузка файлов" на практике держится на двух простых идеях: не доверять файлу и не доверять имени файла. Имя может содержать странные символы, попытки обхода путей, совпадать с системными именами. Расширение и заявленный тип тоже ничего не доказывают: файл с .jpg может оказаться чем угодно.
Защита почти всегда получается многослойной: проверка на входе (размер, тип, количество, скорость), безопасное хранение (не рядом с кодом, уникальные имена, изоляция по пользователям), безопасная выдача (правильные заголовки, запрет исполнения, контроль доступа). Часто добавляют временный доступ через подписанные URL вместо вечных публичных ссылок, а также сканирование и карантин для подозрительных файлов.
Если вы собираете приложение на платформе вроде TakProsto, эти правила все равно применимы: автоматизация ускоряет разработку, но политика доступа, лимиты и способ выдачи файлов остаются ответственностью продукта.
Мини-модель угроз: что нужно защитить
Безопасная загрузка файлов часто ломается не из-за сложных атак, а из-за простых допущений: "проверим расширение и хватит". Мини-модель угроз помогает заранее решить, какие проверки обязательны, а какие можно отложить.
Кто атакует и зачем
Обычно есть три класса нарушителей: анонимы, которые пытаются залить что угодно и найти слабое место; обычные пользователи, которые не обязательно злые, но будут загружать огромные файлы, дубликаты или "не то"; инсайдеры (или взломанные аккаунты), у которых уже есть доступ и больше шансов добраться до чужих данных.
Цели у них тоже приземленные:
- выполнить код или подсунуть активный контент (скрипт в HTML, макросы, полифайлы)
- украсть данные через доступ к чужим файлам или угадывание ссылок
- занять место и ресурсы (память, диск, трафик), устроить отказ в обслуживании
- подменить файл (например, заменить договор после согласования)
Где атакуют
Поверхности атаки обычно три. Первая - форма и API загрузки: обход лимитов, параллельные загрузки, неправильный Content-Type. Вторая - хранилище: предсказуемые имена, общий бакет, отсутствие изоляции по пользователям, возможность перезаписи. Третья - выдача и просмотр: если файл открывается в браузере как HTML или SVG, он может выполниться в контексте вашего домена.
Главная мысль: безопасная загрузка файлов - это не одна проверка, а цепочка решений. Расширение и "разрешенные типы" полезны, но сами по себе не защищают от подмены содержимого, XSS при просмотре и утечек через права доступа.
Архитектура: куда загружать и как отдавать файлы
Самый частый провал в теме "безопасная загрузка файлов" - не вирусы и не расширения, а архитектура. Если хранить загрузки рядом с кодом сайта и раздавать их как обычные страницы, файлы пользователей становятся частью приложения.
Есть два рабочих подхода: загрузка через ваш сервер или прямая загрузка в файловое хранилище.
Загрузка через ваш сервер
Файл сначала приходит на backend. Вы проверяете права, размер и тип, сохраняете, пишете метаданные в базу и только потом отвечаете. Это проще контролировать и логировать: видно, кто, что и когда загрузил.
Минус в том, что вы платите трафиком и ресурсами сервера. Большие файлы легко забивают каналы, память и диски, если нет жестких лимитов и таймаутов.
Прямая загрузка в хранилище
Клиент загружает файл сразу в объектное хранилище (например, S3-подобное), а сервер выдает разрешение на конкретную операцию. Это быстрее и дешевле по трафику на backend.
Цена за скорость - необходимость аккуратно работать с подписанными URL и политиками доступа. Ошибка в сроке действия, путях или правах может открыть чужие файлы или дать возможность перезаписывать объекты.
Где хранить: держите загрузки в отдельном хранилище или хотя бы в отдельном бакете/префиксе, с отдельными учетными данными и минимальными правами. Не храните их в папке, откуда фронтенд и сервер раздают статику, и не смешивайте с исходниками.
Как отдавать: по умолчанию безопаснее выдавать файлы как скачивание, а не как "открыть в браузере". Когда файл открывается inline, браузер может выполнить активное содержимое или показать документ так, что он будет выглядеть частью вашего сайта. Если нужен предпросмотр, лучше делать его через контролируемый слой (например, конвертацию в безопасный формат) или через отдельный домен для раздачи.
Пошагово: базовый поток безопасной загрузки
Безопасная загрузка файлов начинается не с антивируса, а с понятного потока: кто загружает, куда именно прикрепляется файл, где он хранится и как потом выдавать доступ. Если этот поток зафиксирован, риск случайных дыр заметно падает.
Практичный базовый сценарий для небольшого сервиса и для продукта с кабинетами пользователей выглядит так:
-
Проверьте пользователя и цель загрузки. Убедитесь, что у него есть право прикреплять файл именно к этому объекту (заказу, задаче, профилю) и что объект существует.
-
Проверьте размер до приема данных и после. До загрузки ориентируйтесь на заявленный размер (например, Content-Length), а после сравните фактический размер с лимитом. Несовпадение или превышение - повод оборвать загрузку и пометить попытку.
-
Нормализуйте имя файла, но не доверяйте ему. Оригинальное имя храните только как метку для интерфейса. Для хранилища генерируйте безопасный ключ (например, UUID + дата) и не используйте путь от клиента.
-
Запишите метаданные в базу. Минимум: владелец, к чему прикреплено, оригинальное имя, безопасный ключ, размер, тип (как минимум по расширению), хеш (например, SHA-256) и статус. Статусы помогают отделить "принят" от "разрешен к выдаче": pending, uploaded, scanning, ready, rejected.
-
Верните клиенту понятный ответ. Обычно это id файла и текущий статус, плюс как получить доступ позже (например, через отдельный запрос на выдачу доступа). Не обещайте, что файл уже доступен, если он еще не прошел проверку.
Пример: пользователь загружает PDF к заявке. Бэкенд принимает файл, кладет его в хранилище под ключом вида 2026/01/<uuid>, в PostgreSQL сохраняет владельца и статус "scanning", а в интерфейсе показывается "файл принят, идет проверка". Такое разделение стадий часто спасает от первой неприятной истории.
Права доступа и изоляция данных
Если вы делаете безопасную загрузку файлов, начните не с антивируса, а с простого правила: по умолчанию все приватно. Файл не должен быть доступен по публичному адресу только потому, что он успешно загрузился.
Проверяйте права на каждое действие, а не только на вход в раздел. Пользователь, который может открыть страницу проекта, не обязан автоматически уметь скачивать все вложения в этом проекте. У действий разный риск и разная логика.
Полезно мыслить не "файлами", а операциями: загрузить (кто может добавлять), посмотреть (кто видит факт существования и метаданные), скачать (кто получает содержимое), удалить или заменить (кто может убирать и менять версии).
Изоляция по арендаторам или командам должна быть жесткой. Не смешивайте идентификаторы в путях и не полагайтесь на угадываемые номера. Привязывайте файлы к tenant_id или team_id и проверяйте контекст при каждом запросе. Если есть команды и проекты, файл из команды A не должен быть доступен из команды B даже случайно, даже если кто-то подставил другой id.
Практический пример: сотрудник загружает договор в рабочее пространство команды. Если ссылка на скачивание строится только по file_id, другой пользователь сможет перебрать id и получить чужие документы. Если же скачивание всегда требует проверки пары (team_id, file_id) и ролей, перебор перестает работать.
Не забывайте про логи доступа. Записывайте минимум: кто скачивал, когда, откуда (IP или устройство), какой файл и в каком контексте (команда, проект). Эти события нужны не только для расследований, но и как ранний сигнал: массовые скачивания за минуту часто заметны по журналу раньше, чем по жалобам.
Для платформ командной разработки вроде TakProsto это особенно важно: один аккаунт может состоять в нескольких командах, и границы между ними должны оставаться непроницаемыми на уровне API, хранения и выдачи файлов.
Лимиты размера и защита от переполнения
Размер загрузки - это не только про экономию диска. Без жестких ограничений один запрос может съесть память, забить временный каталог, занять воркеры и положить приложение.
Ставьте лимиты на двух уровнях: на один файл и на весь запрос. В multipart-запросе "маленькие файлы" легко превращаются в гигантский запрос из сотен частей, поэтому ограничивайте общий размер тела и число частей.
Что ограничивать в первую очередь
Важно, чтобы ограничения срабатывали до того, как файл будет полностью принят сервером:
- максимальный размер одного файла (например, 10-50 МБ, по задаче)
- максимальный размер запроса (с учетом multipart и служебных полей)
- максимальное количество файлов в одном запросе
- квоты на пользователя или команду (например, 2 ГБ в месяц и 200 МБ в сутки)
- частоту загрузок (rate limit), чтобы скрипт не выжег ресурсы
Квоты лучше считать по факту успешной записи, а не по заявленному размеру. Иначе злоумышленник накрутит учет, отправляя оборванные загрузки. Если у вас командные пространства, квоты на команду защищают от ситуации, когда один участник съедает весь общий лимит.
Отдельно продумайте временные данные. При загрузке файлы часто пишутся во временную папку или буферизуются. Настройте лимиты на размер временного каталога и время жизни временных объектов, а также прерывайте чтение сразу после превышения лимита.
Если лимит превышен, отвечайте понятной ошибкой: сколько можно и что сделать (уменьшить файл, разбить архив, очистить место). При этом важно безопасно удалить временный файл и закрыть поток, чтобы не оставлять хвосты на диске.
Пример: пользователь пытается загрузить 300 МБ логов, а лимит 50 МБ. Правильное поведение - отклонить запрос на раннем этапе, удалить временный кусок и не создавать запись "файл загружен" в базе. На платформах вроде TakProsto это особенно важно, чтобы один проект не выбил ресурсы у других.
Подписанные URL: безопасная выдача доступа
Подписанный URL нужен, когда файл лежит в хранилище отдельно от приложения, а доступ должен быть временным и строго ограниченным. Вместо того чтобы открывать папку целиком или раздавать постоянный адрес, вы выдаете ссылку, где условия запроса подписаны секретом. Сервер проверяет подпись и решает, можно ли выполнить операцию.
Типичный пример: пользователь загрузил договор, и вы хотите дать ему скачать файл только после входа в аккаунт. Приложение проверяет права, а затем выдает подписанную ссылку на скачивание, действующую несколько минут.
Срок жизни делайте коротким и понятным: минуты, а не дни. Это снижает ущерб, если ссылку переслали в чат или она утекла из логов. Если нужно скачать позже, проще выдать новую ссылку после повторной проверки прав.
Что именно подписывать
Смысл подписи не только в "путь + срок", а в том, чтобы запретить подмену условий запроса. Обычно в подпись включают:
- путь (или ID объекта) и версию файла
- пользователя (user_id) или tenant_id
- метод (GET для скачивания, PUT/POST для загрузки)
- срок действия (exp) и, при необходимости, одноразовый nonce
- ограничения: максимальный размер, ожидаемый Content-Type
Если вы выдаете подписанный URL на загрузку, подпишите лимит размера и запретите смену пути. Иначе злоумышленник сможет залить гигабайты или перезаписать чужой объект.
Как отзывать доступ
Подписанные ссылки живут до истечения срока, поэтому отзыв нужно продумать заранее:
- смена ключа подписи (грубо, но быстро при инциденте)
- версионирование объекта: ссылка привязана к версии, новая версия делает старую бесполезной
- статус файла в базе (например, "на проверке", "заблокирован") и отказ в выдаче ссылки
На практике удобно хранить файл по неугадываемому ключу, а подписанные URL выдавать только через бэкенд, где проверяются права и статус. В TakProsto это обычно решают в Go-бэкенде, а фронт получает уже готовую краткоживущую ссылку.
Базовые схемы проверки на вредоносные файлы
Проверка загрузок на вредоносное ПО почти всегда нужна, даже если вы принимаете только "безопасные" форматы. Реальная проблема не в расширении, а в том, что внутри может быть что угодно: макросы, эксплойты, вложенные архивы, или файл, который ломает обработчик.
Синхронно или асинхронно
Синхронная проверка (в том же запросе) подходит только для маленьких файлов и простых сценариев. У нее два минуса: пользователю придется ждать, а сервер держит соединение и ресурсы. Часто это заканчивается таймаутами и подвисающими загрузками.
Асинхронная проверка обычно надежнее. Вы принимаете файл, кладете его в карантин, ставите задачу в очередь и сразу отвечаете статусом "на проверке". Пользователь видит процесс, а вы не рискуете уронить загрузку из-за долгого скана.
Карантин и очередь задач
Карантин - это простое правило: файл нельзя скачать или открыть, пока он не отмечен как чистый. Даже если файл уже лежит в хранилище, выдача блокируется на уровне приложения (или вы храните его в отдельном бакете/папке, недоступной для раздачи).
Практичная схема статусов:
- pending: файл принят, еще не проверен
- clean: проверка пройдена, доступ можно выдавать
- suspicious: найдено подозрительное, доступ запрещен, нужна ручная проверка или удаление
- failed: сканер не сработал (временная ошибка), файл остается в карантине
Пример: сотрудник загружает "счет.pdf". Файл сохраняется как pending и недоступен по ссылке. Через несколько секунд задача в очереди проверяет файл. Если clean, доступ открывается. Если suspicious, пользователь получает понятное сообщение, а файл остается закрытым.
Что хранить по результатам проверки
Сохраняйте не только "чисто/не чисто", но и контекст, чтобы разбирать инциденты и спорные случаи.
Минимальный набор:
- итоговый статус и причина (код/сообщение)
- имя и версия движка/правил сканера
- время сканирования и длительность
- хеш файла (например, SHA-256) и размер
- кто загрузил и откуда (идентификатор пользователя, проект, IP)
Если сканер временно недоступен, не открывайте доступ "на авось". Лучше оставить файл в failed и повторить проверку по расписанию, чем выдать непроверенное содержимое.
Типы файлов и безопасная выдача в браузер
Даже если загрузка прошла через права, лимиты и проверку, проблемы часто начинаются на этапе выдачи: браузер может попытаться исполнить файл, а не просто скачать. Поэтому безопасная загрузка файлов - это еще и про то, как вы определяете тип и какие заголовки ставите.
Не доверяйте расширению. Пользователь легко загрузит invoice.pdf.exe или переименует HTML в JPG. Проверяйте хотя бы два сигнала: заявленный MIME (из запроса) и "магические байты" (сигнатуру первых байтов файла). Если они конфликтуют, лучше отклонить файл или пометить как небезопасный и отдавать только на скачивание.
Что лучше запретить для предпросмотра
Для встроенного просмотра в браузере опасны форматы, где возможен скрипт или активный контент. Обычно для inline-отдачи блокируют:
- HTML/HTM и любые XML-подобные страницы
- SVG (часто несет скрипты или внешние ссылки)
- JS, а также PDF с активными элементами, если нет строгой обработки
- Office-документы с макросами (DOCM, XLSM)
Если формат попадает в серую зону, безопаснее заставить браузер скачать файл.
Заголовки, которые спасают от сюрпризов
Для небезопасных или неизвестных типов отдавайте файл с Content-Disposition: attachment; filename="...", чтобы браузер не пытался открыть его как страницу. Дополнительно полезно выставлять Content-Type: application/octet-stream для неизвестных типов и X-Content-Type-Options: nosniff, чтобы снизить риск MIME-sniffing.
Отдельная тема - изображения. Если вы показываете загруженные картинки другим людям (например, аватар), лучше пересохранить их на сервере в безопасный формат (JPEG/PNG/WebP) и, при необходимости, удалить метаданные. EXIF часто содержит геолокацию и модель устройства. Пример: пользователь загрузил "фото товара", а в EXIF остались координаты дома. Пересохранение решает это без лишних настроек для человека.
Принцип простой: только строго разрешенные типы показывайте inline, все остальное отдавайте как скачивание, даже если файл прошел проверку при загрузке.
Частые ошибки и ловушки
Большинство инцидентов с загрузками начинается не со сложных атак, а с пары удобных решений, которые потом тяжело откатить.
Первая ошибка - доверять имени файла. Пользователь может прислать passport.jpg.exe, а логика возьмет "расширение после точки" или сохранит имя как есть. В итоге появляются странные имена, коллизии, обход фильтров, а иногда и проблемы при скачивании. Безопаснее генерировать свое имя (UUID) и хранить оригинальное отдельно как метаданные.
Вторая ловушка - складывать загрузки в публичную папку и раздавать по прямому пути. Как только файл доступен по адресу, контроль прав легко ломается: кто-то угадал путь, кто-то переслал ссылку, где-то закешировалось. Даже "секретная" структура папок редко остается секретной.
Третья ошибка - надеяться, что антивирус на сервере сам все проверит. Часто он не подключен к вашему потоку загрузки, не видит объекты в S3-совместимом хранилище или проверяет уже после того, как файл стал доступен. Проверка должна быть явным шагом, а выдача - только после результата.
Четвертая - забыть про временные файлы и незавершенные загрузки. Обрывы соединения, повторные попытки и зависшие части забивают диск и превращают лимиты в фикцию.
Пятая - выдавать доступ по предсказуемым ID без проверки прав. Если URL выглядит как /files/12345, его начнут перебирать. Проверка нужна на каждый запрос: кто пользователь, к какому объекту он просит доступ, и разрешено ли это.
Памятка, которая почти всегда помогает:
- генерируйте серверное имя файла и храните оригинальное отдельно
- не раздавайте загрузки напрямую из публичной директории
- делайте проверку файла явным шагом до выдачи доступа
- убирайте хвосты: TTL для черновиков, очистка временных объектов
- не используйте предсказуемые идентификаторы без проверки прав на каждом запросе
Если вы собираете приложение в TakProsto, заранее заложите эти правила в сценарии: так меньше шанс, что временное решение станет постоянным и опасным.
Быстрый чеклист и следующие шаги
Перед релизом полезно пройтись по короткому набору проверок, чтобы загрузки не стали первым инцидентом, даже если пользователи будут ошибаться или намеренно тестировать границы.
Перед выкладкой проверьте основу:
- права: кто может загружать, кто может читать, и точно ли чужие файлы не видны по прямому идентификатору
- приватность: хранение не в публичной папке, выдача только через контролируемый доступ
- лимиты: максимальный размер файла, число файлов за запрос, таймауты, защита от бесконечной загрузки
- типы: белый список форматов, запрет опасных расширений и двойных расширений (например, photo.jpg.exe)
- логи: фиксируйте пользователя, IP, размер, тип, результат проверки и причину отказа
В продакшене важно не только запрещать, но и быстро замечать аномалии. Минимальные операционные настройки обычно такие:
- мониторинг: всплески 4xx/5xx на загрузке, рост объема хранилища, очередь проверок, время ответа
- квоты: лимит на пользователя или команду, защита от выжигания диска и бюджета
- очистка: срок жизни временных объектов, удаление неиспользуемых загрузок, ревизия висящих записей в базе
- алерты: уведомления о частых отказах по лимитам, подозрительных типах, повторяющихся попытках
- инцидент-процедура: кто отключает загрузки, как быстро закрыть доступ к уже выданным ссылкам
План улучшений на 30 дней можно держать простым. Неделя 1: стабилизируйте лимиты, права и подпись ссылок с коротким TTL. Неделя 2: добавьте карантин (файл недоступен до проверки) и асинхронную проверку на вредоносное содержимое. Неделя 3: введите квоты и регулярную очистку, настройте алерты. Неделя 4: прогоните негативные тесты (переполнение, подмена MIME-типа, скачивание чужого файла) и проверьте восстановление после сбоев.
Следующий шаг: зафиксируйте поток загрузки как короткий план и прогоните его на тестовом окружении. В TakProsto это удобно делать через Planning Mode: описать требования человеческим языком, быстро собрать прототип с хостингом, а затем спокойно экспериментировать со снимками и rollback, если правки сломают сценарий. Если вы ведете несколько проектов и команд, проще держать эти требования рядом с продуктовой логикой, а не в головах разработчиков. Для этого можно использовать takprosto.ai как место, где сценарии и ограничения фиксируются прямо в процессе сборки приложения.
FAQ
С чего начать, если нужно сделать загрузку файлов безопасной?
Начните с двух правил: не доверяйте содержимому файла и не доверяйте имени файла. Дальше выстройте цепочку: проверка на входе (права, лимиты, тип), безопасное хранение (уникальные ключи, изоляция), безопасная выдача (контроль доступа, правильные заголовки, по умолчанию скачивание).
Почему нельзя просто проверять расширение и список разрешенных типов?
Потому что расширение и Content-Type легко подделать. Минимум, что стоит сделать:
- сверить заявленный MIME-тип с реальным (по сигнатуре первых байтов)
- отклонять явные конфликты и «двойные расширения» вроде
photo.jpg.exe - неизвестные форматы отдавать только как скачивание, а не inline
Как правильно обрабатывать имя файла, чтобы не словить проблемы с путями и коллизиями?
Сохраняйте оригинальное имя только как подпись для интерфейса. Для хранения всегда генерируйте серверный ключ, например UUID + дата.
- не используйте путь от клиента
- нормализуйте имя, если показываете его пользователю (уберите спецсимволы)
- не позволяйте перезапись по одному и тому же ключу без явного контроля версии
Как не допустить, чтобы один пользователь скачал чужие файлы по перебору ID?
Дайте доступ только после проверки прав и статуса файла.
- храните файлы приватно по умолчанию
- на скачивание проверяйте пару вроде
(team_id, file_id)и роль пользователя - не делайте URL вида
/files/12345без строгой авторизации на каждом запросе - ведите логи скачиваний (кто, когда, откуда, какой файл)
Какие лимиты нужны, чтобы загрузками не положили сервер и не раздули хранилище?
Ставьте лимиты заранее, чтобы они срабатывали до полного приема данных:
- максимальный размер одного файла
- максимальный размер всего запроса (multipart)
- максимальное число файлов в одном запросе
- rate limit на загрузки
- квоты на пользователя/команду (день/месяц)
После отказа удаляйте временные куски и не создавайте запись «файл загружен».
Что выбрать: загружать через backend или напрямую в хранилище?
Если файлы большие или загрузок много, прямая загрузка в объектное хранилище обычно дешевле по трафику для backend. Если важнее контроль и простота — принимайте через сервер.
В обоих случаях держите загрузки отдельно от кода и статики, с минимальными правами доступа и изоляцией по пользователям/командам.
Как правильно использовать подписанные URL, чтобы не открыть доступ ко всему бакету?
Подписанный URL нужен для временного и строго ограниченного доступа.
Практика:
- делайте TTL коротким (минуты)
- подписывайте путь/объект, метод (GET/PUT), срок, при необходимости nonce
- для загрузки дополнительно фиксируйте лимит размера и ожидаемый тип
- выдавайте ссылку только после проверки прав на backend
Как организовать проверку на вредоносные файлы без таймаутов и подвисаний?
Хороший базовый поток такой:
- файл сохраняется со статусом
pending/scanningи не доступен - фоновая задача сканирует и выставляет
cleanилиsuspicious/failed - скачивание разрешается только при
clean
Если сканер временно не работает, оставляйте файл в карантине и повторяйте проверку, а не открывайте доступ «на авось».
Какие заголовки и правила выдачи помогают избежать XSS при просмотре файлов в браузере?
По умолчанию безопаснее принуждать скачивание.
- опасные типы (HTML, SVG, скрипты, файлы с макросами) не отдавайте inline
- для неизвестного ставьте
Content-Type: application/octet-stream - добавляйте
Content-Disposition: attachment; filename="..." - включайте
X-Content-Type-Options: nosniff, чтобы снизить риск MIME-sniffing
Если я делаю приложение в TakProsto, что из этого мне все равно нужно настроить самому?
Да, автоматизация ускоряет сборку, но политика доступа и ограничения задаете вы.
Что обычно стоит явно заложить в сценарии TakProsto:
- приватное хранение и выдача только через проверку прав
- лимиты размера/частоты и квоты на команду
- статусы файла и карантин до проверки
- выдача через краткоживущие подписанные ссылки
- возможность отката настроек и логики через снимки и rollback, если правки сломали поток