Как устроено · s5w

Безопасность

Кто и к чему имеет доступ, как это проверяется и что остаётся на стороне развёртывания.

Кратко

Токены по бакетам

Каждый токен действует только в своём бакете. В базе хранится его хеш, а не значение.

Admin API закрыт снаружи

При включённой панели управляющий API отвечает только самой панели внутри контейнера.

Блокировка перебора

Пять неудачных входов за 15 минут — и адрес блокируется на 5 минут.

Подписанные ссылки

Временные ссылки подписаны ключом сервера: подделать или продлить их нельзя.

Токены доступа

  • Токен выпускается для одного бакета и даёт доступ только к нему.
  • В базе хранится хеш токена. Само значение показывается один раз, при выпуске; восстановить его нельзя — только перевыпустить.
  • Два уровня: BASIC (только проверка и скачивание по токену) и FULL (всё). Выдавайте BASIC тем, кому нужно только читать.
  • Токен можно ограничить сроком, отключить или перевыпустить (ротация). После ротации старое значение перестаёт работать сразу.
  • При каждом запросе проверяется: токен известен, активен, не истёк и его уровня хватает. Ответы — 401 (нет токена или он неизвестен) и 403 (отключён, истёк, мало прав).

Вход в админ-панель

Учётная запись одна, она задаётся переменными ADMIN_LOGIN и ADMIN_PASSWORD.

  • Без них вход закрыт полностью. Это сделано намеренно: панель без учётных данных не открывается никому, а не всем.
  • Сессия живёт 24 часа. Она хранится в куке s5w_admin с флагами HttpOnly и SameSite=Strict: скрипт на странице её не прочитает, а другой сайт не сможет отправить запрос от имени администратора.
  • Сессия подписана ключом WINTER_KEY и привязана к логину и паролю. Смена пароля или ключа сразу завершает все открытые сессии.
  • Блокировка перебора. Пять неудачных входов с одного адреса за 15 минут блокируют этот адрес на 5 минут: вход отвечает 429 с заголовком Retry-After, даже с правильным паролем. Счётчик хранится в памяти и сбрасывается при перезапуске.
  • Адрес клиента за прокси берётся из X-Forwarded-For, но только от доверенных прокси (TRUSTED_PROXIES). Иначе любой мог бы прислать чужой адрес и обойти блокировку.

Пароль по умолчанию

В примерах стоит change-me. В рабочем развёртывании задайте длинный случайный пароль и храните его как секрет, а не в открытом compose-файле в репозитории.

Admin API

Управляющий API /admin/… создаёт бакеты, токены и всё остальное — это полный доступ к хранилищу.

  • С включённой панелью (по умолчанию) он принимает запросы только изнутри контейнера, то есть от самой панели. Снаружи — 404, как будто его нет.
  • ADMIN_API=public открывает его наружу — например, для скриптов автоматизации. Тогда закройте порт 9090 или путь /admin на прокси для всех, кроме нужных адресов.
  • Без панели (ADMIN_UI=false) он открыт по умолчанию — иначе бакетами было бы нечем управлять. Закройте /admin на уровне сети.

Публичность файлов

Файл либо публичный, либо нет; это определяется папкой.

  • Корень бакета всегда публичный. Файл, загруженный или перенесённый туда, открывается по /o/{bucketId}/{id} без токена.
  • Файл в папке получает видимость папки. При переносе между папками видимость меняется на видимость новой папки.
  • Публичный адрес содержит идентификатор бакета и случайный идентификатор файла (96 бит случайности) — его нельзя угадать перебором. Но кто получил ссылку, тот может её передать.

Перенос в корень

Приватный документ, перенесённый в корень, становится доступен всем, у кого есть адрес. Держите закрытые файлы в приватных папках.

Временные ссылки

  • Ссылка подписана ключом WINTER_KEY: срок, файл и параметры зашиты в подпись, изменить их нельзя.
  • Учитываемую ссылку (revocable или maxDownloads) можно отозвать по одной.
  • Все ссылки бакета, включая подписанные, отзываются одним запросом DELETE /v1/links или кнопкой в панели.
  • Смена WINTER_KEY делает недействительными все временные ссылки во всех бакетах.

Загруженное содержимое

  • HTML и SVG никогда не открываются в браузере — только скачиваются. Показанные браузером, они могли бы выполнить скрипт от имени вашего домена.
  • В браузере открываются только картинки, видео, аудио, PDF и простой текст; всё остальное отдаётся на скачивание.
  • Имя файла очищается: разделители путей и управляющие символы заменяются на -. В заголовок Content-Disposition имя попадает экранированным, подставить через него лишний заголовок нельзя.
  • На диск содержимое попадает под именем-хешем: имя от клиента не участвует в путях.

Что остаётся на развёртывании

  • TLS. Сам S5W говорит по HTTP. Шифрование делает прокси перед ним — nginx-proxy-manager, traefik, nginx.
  • Сетевой доступ. Не публикуйте порты, которые не нужны. Домен панели стоит дополнительно закрыть списком доступа или basic auth.
  • Секреты. ADMIN_PASSWORD и, если вы задаёте его сами, WINTER_KEY — храните как секреты.
  • Резервные копии тома — они содержат все файлы и ключ подписи, защищайте их так же, как сами данные.