Перейти до вмісту
Обрати мову:Українська

Модель безпеки

Ця сторінка — підсумок моделі загроз Kept та механізмів, що стоять за нею, для оператора. Сторінки для розробників детальніші: захист на рівні рядків, вхід та архітектура.

Домогосподарства розділяються базою даних

Section titled “Домогосподарства розділяються базою даних”

Kept підключається до Postgres чотирма обліковими записами, кожен з яких має лише ті права, що потрібні для його завдання:

Обліковий запис Використовується Що може робити
kept_owner kept migrate, kept admin, нічне резервне копіювання володіє схемою; ніколи не використовується для обслуговування запиту
kept_app кожен веб-запит, включно з маршрутами адміністратора сервера читає й записує таблиці застосунку, завжди під захистом на рівні рядків (NOBYPASSRLS, FORCE RLS на кожній таблиці)
kept_auth вхід (Better Auth) лише власна схема auth
kept_system фонові завдання, що охоплюють кілька домогосподарств (сканування нагадувань, строки зберігання), код налаштування лише явні системні політики

Кожен запит виконується в транзакції, що вказує особу, яка увійшла, і кожна політика запитує одну функцію, до яких локацій належить ця особа. Коли особу не вказано, політики нічого не повертають, тож шлях коду, що забув про область, не отримує доступу (fail closed). Тест на витік по всій схемі перевіряє шляхи запиту, автентифікації та воркера. Деталі — у захисті на рівні рядків.

Що це означає для вас: обслуговуючий контейнер ніколи не зберігає обліковий запис власника, хіба що ви надасте його для резервних копій (резервні копії). Адміністратор сервера не має доступу в застосунку до локацій інших людей; оператор із доступом до бази даних може обійти це, тому сама база даних має залишатися приватною для сервера.

  • KEPT_SECRET_KEY запечатує кожне секретне значення: секретні поля речей, ключі ШІ, налаштування каналів сповіщень, секрети вебхуків, паролі поштових скриньок для вхідної пошти, токени API Paperless-ngx, приватний ключ веб-пушу, пароль резервної копії. AES-256-GCM з ключем даних для кожного рядка, кожен шифротекст прив’язаний до своєї таблиці, рядка та поля, тож один не можна підмінити іншим.
  • KEPT_AUTH_SECRET підписує сесії та токени. Він окремий, тож кожен можна ротувати окремо.
  • Генерується при першому запуску, коли обидва не задані, у том конфігурації (ніколи — у том даних), з одним рядком логу, де саме. Задайте їх самі командою kept admin gen-key; запуск не приймає ключ, коротший за 32 байти, і ключ, заданий без іншого.
  • Жоден із них не у базі даних і не у резервній копії. Зберігайте набір для відновлення. Без секретного ключа відновлення повертає все, крім секретних значень і ключів ШІ.
  • Ротація: kept admin rotate-key робить новий ключ поточним і повторно обгортає кожне запечатане значення; старий ключ зберігається як застаріла версія, щоб старіші резервні копії все ще відкривалися. --resume завершує перерваний запуск, --drop <version> видаляє застарілий ключ, який нічого не використовує (CLI).
  • Паролі поштових скриньок доступні лише для запису: сервер, ім’я користувача та пароль поштової скриньки для вхідної пошти запечатані разом; сторінка показує сервер та ім’я користувача, ніколи — пароль.
  • Ключі ШІ доступні лише для запису: після збереження Kept показує лише їхні останні чотири символи.
  • Токени Paperless-ngx доступні лише для запису: адреса та токен Paperless локації запечатані разом; сторінка показує адресу, ніколи — токен, а браузер ніколи не звертається до Paperless з ним. Мініатюри та завантаження проходять через Kept, після перевірки доступу самим Kept.

Kept використовує Better Auth із власним логіном і власною схемою. З чим може входити обліковий запис:

  • Електронна пошта та пароль. Скидання пароля виводить обліковий запис із системи скрізь, і власнику повідомляють електронною поштою, коли змінюються облікові дані.
  • Ключі доступу, зареєстровані для імені хоста KEPT_PUBLIC_URL. Кожен має підтверджувати користувача (Face ID, відбиток пальця, PIN) і бути виявлюваним.
  • Двофакторна автентифікація із застосунком-автентифікатором (TOTP) або ключем доступу, що підтвердив користувача. Одноразовий код, надісланий електронною поштою, ніколи не є другим фактором. Власник локації може вимагати двофакторну автентифікацію для всіх у ній. Облікові записи без пароля також можуть користуватися застосунком-автентифікатором.
  • Налаштування → Вхід і безпека додає, перейменовує та видаляє ключі доступу, вмикає застосунок-автентифікатор, створює нові резервні коди, налаштовує його заново та вимикає (Вхід і безпека). Кожна зміна вимагає пароль або вхід за останні десять хвилин для облікового запису без пароля, і виводить із системи на всіх інших пристроях (окрім перейменування). Сервер відмовляється видаляти останній спосіб входу, а також вимикати двофакторну автентифікацію, якщо локація вимагає її, а обліковий запис не має ключа доступу. Повторне налаштування застосунку залишає старий секрет робочим, доки не буде доведено код з нового.
  • Магічні посилання лише для наявних облікових записів (без відкритої реєстрації), дійсні 15 хвилин. Надіслані токени передаються у #fragment посилання і витрачаються POST-запитом зі сторінки, тож сканер пошти, що відкриває посилання, не може їх використати; зберігається лише хеш.
  • Імена користувачів для керованих облікових записів (людей без адреси електронної пошти).
  • OIDC, коли задані KEPT_OIDC_ISSUER та KEPT_OIDC_CLIENT_ID (конфігурація). Зовнішня ідентичність ніколи не прив’язується до облікового запису лише тому, що збігається електронна пошта; нова особа потрапляє за запрошенням або через дозволений домен чи групу. Токени постачальника ідентичності ніколи не зберігаються.
  • Google, коли задані KEPT_GOOGLE_CLIENT_ID та KEPT_GOOGLE_CLIENT_SECRET, з URI перенаправлення, який показує Адміністрування → Статус. Ті самі правила, що й для OIDC, крім того, що ніхто не потрапляє за доменом: лише наявний обліковий запис, що прив’язав Google, або запрошена особа.
  • Прив’язка та відв’язка зовнішнього входу відбуваються в Налаштування → Я → Способи входу. Прив’язка вимагає входу за останні десять хвилин; відв’язка запитує пароль (або цей нещодавній вхід для облікового запису без пароля). Обліковий запис зберігає принаймні один спосіб входу.

Сесії тривають 30 днів через HTTPS, продовжуються раз на день використання, з cookie Secure із префіксом __Secure-. Через звичайний HTTP вони тривають 12 годин і ніколи не продовжуються, а Kept не виконує дій, що розкривають або змінюють секрет (HTTPS). Чутливі дії знову вимагають пароль або вхід за останні десять хвилин; поновлення входу не скидає ці десять хвилин.

KEPT_HTTP_SIGN_IN_DAYS (від 1 до 30, за замовчуванням не задано) дає тому, хто запускає сервер, зробити так, щоб вхід через звичайний HTTP тривав стільки днів, поновлюючись у міру використання. Це єдине налаштування, що послаблює це обмеження, його не можна змінити з застосунку, і через HTTPS воно нічого не робить. Дії із секретами через HTTP все одно відхиляються, десять хвилин лишаються, а вихід скрізь все одно завершує кожен вхід. Ціна: через звичайний HTTP cookie входу може скопіювати будь-хто, хто бачить трафік, а з увімкненим налаштуванням скопійований продовжує працювати стільки, скільки використовується, поки той пристрій не вийде або інші ваші пристрої не будуть виведені (Залишаючись у своїй мережі).

Запити з інших сайтів відхиляються: запис, що несе cookie Kept, має надходити з походження KEPT_PUBLIC_URL, інакше він отримує 403 ще до того, як сесію прочитають. Те саме стосується налаштування при першому запуску, де cookie ще немає: відмова настає раніше, ніж спробують його код налаштування.

KEPT_EXTRA_ADDRESSES (за замовчуванням не задано) дає тому, хто запускає сервер, перелічити інші точні походження, що вважаються Kept. Їх читають один раз при запуску і порівнюють повністю з Origin браузера; Host та X-Forwarded-* не відіграють ролі. Шаблон (wildcard) або суміш http і https зупиняє запуск. Відмова називає KEPT_PUBLIC_URL і ніколи не називає список. Ключі доступу, зовнішні входи, OAuth і всі посилання лишаються на KEPT_PUBLIC_URL: ключ доступу перевіряється щодо тієї адреси, що б запит не стверджував. Ціна: сторінка, що віддається за переліченою адресою, може діяти від імені того, хто там увійшов (Більше ніж одна адреса).

Сторінки віддаються з політикою безпеки вмісту default-src 'self', їх не можна вставляти у фрейми, і вони отримують HSTS, коли публічна адреса використовує HTTPS, якщо тільки не KEPT_HSTS=off (HSTS).

Як реалізовано:

Що Обмеження
Спроби входу 5 на хвилину на адресу; на обліковий запис і адресу — 20 на годину, із затримками, що зростають після четвертої невдачі (до 5 хвилин), замість блокування
Інші маршрути автентифікації 100 на хвилину на адресу та маршрут
API та MCP, на токен 120 читань і 30 записів на хвилину
Скидання пароля та магічне посилання 3 на годину на обліковий запис
Сторінка посилання для спільного доступу 60 переглядів на хвилину на посилання

Обмеження зберігаються у базі даних, тож вони спільні для всіх реплік. Адреса клієнта береться з сокета або з X-Forwarded-For лише тоді, коли з’єднання надходить з адреси у KEPT_TRUSTED_PROXIES (зворотні проксі).

Kept звертається лише до адрес, які хтось налаштував: базової адреси постачальника ШІ, вебхуків, пушу, видавця OIDC, метаданих клієнта OAuth, сервера Homebox для імпорту, пошуку штрих-кодів, перевірки оновлень, поштової скриньки для вхідної пошти локації (IMAP) і Paperless-ngx локації. Кожна проходить через той самий захист (apps/server/src/net/ssrf.ts):

  • адреса перевіряється у момент встановлення з’єднання, після розв’язання імені, тож ім’я, що змінюється між перевіркою та запитом, не пройде;
  • приватні, loopback, link-local (включно з адресою метаданих хмари), carrier-grade NAT, multicast і зарезервовані діапазони відхиляються, для IPv4 та IPv6;
  • перенаправлення відхиляються, тож публічний хост не може спрямувати запит усередину.

Відхилена адреса повертає 400 private_address. На самостійно розміщеному сервері адміністратор сервера може дозволити приватні адреси (Адміністрування → Налаштування → Дозволити приватні адреси), що потрібно для постачальника у вашій мережі, такого як Ollama. За замовчуванням вимкнено.

Імпорти та завантаження розглядаються як ворожі. Значення за замовчуванням:

Обмеження За замовчуванням
Імпорт ZIP 5 GB без стиснення, стиснення 100:1 на запис, 200 000 записів, без символічних посилань
Зображення декодуються до 100 мегапікселів, по одному за раз за RAM до 3 GB або контейнерне обмеження до 3 GB
PDF розбираються у дочірньому процесі, 20 с і 256 MB
Завантаження KEPT_MAX_FILE_MB (25 MB за замовчуванням), відхиляються до читання тіла

Збережені файли отримують через підписане посилання на п’ять хвилин за шляхом, що не читає сесію, з політикою nosniff і default-src 'none'; sandbox, і як завантаження, якщо файл не є одним із власних повторно кодованих JPEG Kept. Експортовані комірки CSV, що починаються як формула, нейтралізуються.

  • Нічого про сам Kept. Жодної телеметрії взагалі; телеметрію самої бібліотеки входу вимкнено. Трасування та звітування про помилки йдуть лише до колектора, який ви задали (моніторинг).
  • Перевірка оновлень за замовчуванням вимкнена. Увімкнена, вона раз на день запитує GitHub про найновіший реліз, надсилаючи лише запит (оновлення).
  • Що бачить постачальник ШІ, коли локація його використовує: фото як повторно кодована копія з видаленими EXIF і XMP (Kept відмовляється надсилати зображення, яке їх ще містить), фіксований промпт і мови локації для вилучення, питання та результати інструментів, які дозволяє роль того, хто запитує, для помічника, а також описові поля для семантичного пошуку. Ніколи секретних значень і ніколи грошей для пошуку. Власний запис Kept про кожен виклик зберігає токени та вартість, ніколи — промпт, зображення, відповідь чи ключ. Див. Постачальники ШІ.
  • Посилання для спільного доступу (/s/<token>) — єдина сторінка, яку може відкрити будь-хто без входу. Токен — 32 випадкові байти, показується один раз і зберігається як його SHA-256; він ніколи не потрапляє у рядок логу. Сторінка читається через одну функцію бази даних, що приймає хеш токена і повертає лише назви, типи, де розташовані речі, кількість, стан, примітки та фото для спільної коробки, місця чи пошуку в тій локації. Фото — прев’ю з видаленим GPS, як підписані URL на п’ять хвилин, і лише для тих речей. Посилання припиняє діяти, коли його відкликано або спливає строк, або коли його автор більше не адмініструє локацію; перевірку виконують при кожному перегляді.
  • Резервні копії — знімки restic, зашифровані паролем не коротшим за 12 символів ще до того, як вони покинуть сервер; без пароля резервної копії немає (резервні копії).

Повідомлення про вразливість

Section titled “Повідомлення про вразливість”

Використовуйте приватне повідомлення про вразливість GitHub у репозиторії (вкладка Security → Report a vulnerability); це єдиний канал, і адреси електронної пошти для безпеки немає. Не відкривайте публічного іш’ю. SECURITY.md містить деталі та підтримувані версії, які діють починаючи з 1.0. Щоб перевірити, що образ — той, що зібрав мейнтейнер, див. перевірка релізу.