Перейти к содержанию

B09 · Безопасность#

Блок про то, кто и что может делать в системе, и про то, от чего это защищает. Первая половина — аутентификация: сессии, токены, OAuth, пароли. Вторая — авторизация, атаки, от которых всё это защищает, и цепочка поставок, ставшая основным вектором компрометации.

Порядок в блоке:

  • «кто ты» — SEC-01–SEC-03;
  • «что тебе можно» — SEC-04;
  • защита учётных данных и границ — SEC-05–SEC-09;
  • процесс: цепочка поставок, моделирование угроз, аудит — SEC-10–SEC-12;
  • классификация данных и режим защиты — SEC-13.

Навигация: SEC-01 · SEC-02 · SEC-03 · SEC-04 · SEC-05 · SEC-06 · SEC-07 · SEC-08 · SEC-09 · SEC-10 · SEC-11 · SEC-12 · SEC-13


SEC-01 · Аутентификация: сессии против токенов#

Шаг W8

TL;DR. Сессия — идентификатор, по которому сервер находит состояние у себя, токен — само состояние, подписанное сервером. Разница в том, где живёт истина и как быстро её можно отозвать.

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

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

  • Серверная сессия. Клиент получает непредсказуемый идентификатор в cookie, состояние лежит в общем хранилище (CACHE-05). Отзыв мгновенный — достаточно удалить запись. Цена — обращение к хранилищу на каждый запрос и его доступность как критическая зависимость.
  • Токен (SEC-02) несёт данные о пользователе и подпись. Сервер проверяет подпись, никуда не ходя, — это масштабируется и подходит для нескольких сервисов. Цена — токен нельзя отозвать, он действует до истечения срока.
  • Практический компромисс. Короткоживущий access token (токен доступа) на минуты плюс долгоживущий refresh token (токен обновления), который хранится на сервере и отзывается. Отзыв срабатывает в пределах срока жизни access token — компромисс между мгновенностью и независимостью проверки.
  • Где хранить на клиенте. Cookie с флагами HttpOnly, Secure и SameSite недоступна скриптам, поэтому переживает XSS (межсайтовый скриптинг, SEC-07), но требует защиты от CSRF (подделки межсайтовых запросов). Токен в памяти страницы не подвержен CSRF, но крадётся скриптом. Хранилище браузера (localStorage) — худший вариант: доступно любому скрипту и переживает перезагрузку.

Многофакторность и вход. Второй фактор относится к моменту входа, а не к каждому запросу; после проверки создаётся обычная сессия. Поток входа отдельно защищается от перебора (EDGE-06).

Сравнение

Серверная сессия Токен
Где живёт истина В хранилище сервера В самом токене
Проверка запроса Обращение к хранилищу Локальная проверка подписи
Отзыв доступа Мгновенный Только по истечении срока
Общее состояние Нужно Не нужно
Когда уместно Одно приложение, важен мгновенный выход Несколько сервисов, интеграции, мобильные клиенты

Trade-offs и режимы отказа. Сессии в памяти процесса ломают горизонтальное масштабирование (DIST-01) и заставляют включать sticky sessions (липкие сессии). Долгий срок жизни токена делает утечку долгоиграющей проблемой. Отсутствие ротации идентификатора после входа даёт session fixation (фиксацию сессии): злоумышленник подсовывает свой идентификатор до входа и получает аутентифицированную сессию после.

Что мерить.

  • долю неуспешных входов и всплески перебора;
  • число активных сессий;
  • задержку проверки;
  • время от отзыва до фактического прекращения доступа.

Связано: SEC-02 JWT · SEC-03 OAuth · SEC-05 пароли · NET-19 CORS и cookie

Источники: OWASP — Session Management Cheat Sheet · OWASP — Authentication Cheat Sheet · ByteByteGo — Session Vs JWT: The Differences You May Not Know!


SEC-02 · JWT: устройство и грабли#

Шаг W8

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

Как устроен. Три части: заголовок (header) с алгоритмом, полезная нагрузка (payload) с утверждениями (claims) — кто, кем выдан, кому предназначен, до какого времени, — и подпись. Части закодированы, но не зашифрованы: содержимое читает кто угодно. Подпись — симметричная общим секретом либо асимметричная, когда выдающая сторона подписывает закрытым ключом, а проверяющие используют открытый. Второй вариант лучше, когда проверяющих много.

Грабли, каждая из которых была реальной уязвимостью.

  • Доверие к полю алгоритма. Проверяющая сторона должна требовать ожидаемый алгоритм, а не брать его из самого токена. Иначе alg: none отключает проверку подписи, а подмена асимметричного алгоритма симметричным превращает открытый ключ в секрет подписи.
  • Непроверенные адресат (aud) и издатель (iss). Токен, выданный для другого сервиса, должен отвергаться: иначе он работает везде.
  • Отсутствие срока или слишком длинный срок. Токен нельзя отозвать; единственный ограничитель — время жизни, и оно должно измеряться минутами.
  • Чувствительные данные внутри. Содержимое читаемо: персональные данные в утверждениях — утечка.
  • Ротация ключей. Идентификатор ключа в заголовке (kid) и публикуемый набор ключей (JWKS, JSON Web Key Set) позволяют менять ключи без простоя. Без этого ротация означает разлогинивание всех.
  • Токен в адресной строке попадает в логи, историю браузера и заголовок источника перехода (Referer). Передавать токен — только в заголовке Authorization.

Когда применять. Межсервисная передача идентичности, API без общего состояния, интеграции. Когда нет: классическое веб-приложение с одним бэкендом обслуживается серверной сессией проще и с мгновенным отзывом (SEC-01).

Trade-offs и режимы отказа. Список отозванных токенов (denylist) возвращает обращение к общему хранилищу — то самое, от которого уходили, выбирая токены. Раздувание утверждений увеличивает каждый запрос и упирается в лимиты заголовков. Clock skew (расхождение часов) между выдающей и проверяющей сторонами приводит к отказам на границе срока, поэтому допускают небольшой запас.

Что мерить.

  • долю отказов по причинам: истёк, неверная подпись, неверный адресат;
  • время жизни выданных токенов;
  • размер токена;
  • задержку обновления набора ключей.

Связано: SEC-01 аутентификация · SEC-03 OAuth и OIDC · SEC-08 ключи

Источники: RFC 7519 — JSON Web Token · RFC 8725 — JWT Best Current Practices · OWASP — JWT Cheat Sheet · ByteByteGo — Why is JWT popular?


SEC-03 · OAuth 2.0 и OIDC#

Шаг W8

TL;DR. OAuth решает делегирование доступа: приложение действует от имени пользователя, не зная его пароля. OIDC (OpenID Connect) — надстройка, которая добавляет ответ на вопрос «кто это».

Проблема. Стороннему приложению нужен доступ к вашим данным в другом сервисе. Отдать ему пароль — значит отдать всё и навсегда. Нужен ограниченный по объёму и по времени доступ, который можно отозвать, не меняя пароль.

Роли и поток. Владелец ресурса (пользователь), клиент (приложение), сервер авторизации, сервер ресурсов. Основной поток — authorization code (код авторизации): пользователь входит на сервере авторизации, тот возвращает клиенту одноразовый код, клиент обменивает код на токен по защищённому каналу. Ключевая идея — токен не проходит через адресную строку браузера.

Что важно сегодня.

  • PKCE (Proof Key for Code Exchange) обязателен для всех клиентов, а не только мобильных. Он привязывает обмен кода к тому же клиенту, который его запросил, и закрывает перехват кода.
  • Implicit flow (неявный поток) и password grant (поток с паролем) устарели: первый отдаёт токен через адресную строку, второй требует передачи пароля приложению.
  • Scope (область доступа) — это ограничение объёма, а не полноценная авторизация: сервер ресурсов всё равно проверяет права (SEC-04).
  • OIDC добавляет ID token (токен идентичности) — подписанный JWT с утверждениями о пользователе и обязательной проверкой адресата и издателя. На вопрос «кто вошёл» отвечает OIDC, а не OAuth: использовать access token как удостоверение личности — распространённая ошибка.
  • Проверка redirect URI (адреса возврата) по точному совпадению. Иначе open redirect (открытое перенаправление) превращается в кражу кода.

Trade-offs и режимы отказа. Протокол сложен, и большинство уязвимостей возникает из-за самостоятельной реализации. Надёжнее взять проверенную библиотеку и провайдера. Сервер авторизации становится критической зависимостью: его недоступность блокирует все входы. Слишком широкие scopes выдают приложению больше, чем нужно, и пользователи их не читают.

Что мерить.

  • долю успешных авторизаций по потокам;
  • число активных выданных разрешений;
  • долю отзывов;
  • ошибки проверки redirect URI.

Связано: SEC-01 аутентификация · SEC-02 JWT · SEC-04 авторизация

Источники: RFC 6749 — OAuth 2.0 · RFC 9700 — OAuth 2.0 Security Best Current Practice · OpenID Connect Core · ByteByteGo — OAuth 2 Explained In Simple Terms


SEC-04 · Авторизация: RBAC, ABAC, ReBAC#

Шаг W8

TL;DR. Аутентификация отвечает «кто ты», авторизация — «что тебе можно с этим объектом». Второе сложнее.

Проблема. Проверка «токен валиден» ничего не говорит о праве прочитать конкретный заказ. Самая частая уязвимость веб-приложений — не взлом аутентификации, а отсутствующая проверка на уровне объекта: пользователь меняет идентификатор в адресе и получает чужие данные. В классификациях это IDOR (Insecure Direct Object Reference) и BOLA (Broken Object Level Authorization). Уязвимость возникает не от сложности, а от того, что проверку легко забыть в одном из сотни мест.

Модели.

Модель Решение основано на Когда уместна
RBAC (Role-Based Access Control) — роли Роль пользователя Небольшое число ролей, права по типам объектов
ABAC (Attribute-Based) — атрибуты Атрибуты субъекта, объекта и контекста (отдел, время, сумма) Правила зависят от данных, а не только от роли
ReBAC (Relationship-Based) — отношения Граф связей «пользователь — объект» Общий доступ, вложенные пространства, наследование прав

RBAC разрастается комбинаторно, как только права зависят от объекта: появляются роли вида «редактор проекта 42». Это признак, что нужна другая модель — ABAC для правил по атрибутам, ReBAC для явного графа отношений (DATA-02).

Проверяемый порог вместо ощущения. Выпишите существующие правила и попробуйте выразить их выбранной моделью. Одно исключение — предупреждение, три — модель выбрана не та. Число здесь соглашение, а не замер: оно нужно затем, чтобы спор «у нас пока нормально» имел точку остановки.

Где проверять. Аутентификация может жить на границе (EDGE-03), авторизация — нет: право зависит от предметной области и от конкретного объекта, поэтому проверка выполняется там, где объект известен. Практика — тонкая прослойка доступа к данным, через которую проходят все запросы и которая физически не позволяет получить объект без проверки. Полагаться на дисциплину «не забыть проверку» нельзя: одно забытое место — утечка.

Практические правила.

  • запрещено по умолчанию, разрешение явное (deny by default);
  • одно место принятия решения, а не проверки, разбросанные по контроллерам;
  • право на список и право на объект различаются: фильтрация выдачи по правам должна происходить в запросе к данным, иначе постраничная выдача покажет чужое;
  • проверка на клиенте — часть интерфейса, а не защита.

Куда падать, когда проверяющий недоступен — в запрет. Это fail closed: устаревшее разрешение — утечка, устаревший запрет — неудобство, и цена у них несопоставима. Ровно обратный ответ у rate limiter (ограничителя частоты, EDGE-06): там при недоступности счётчика правильно пропускать (fail open). Направление отказа — свойство операции и того, какой инвариант она держит, а не свойство системы. В одном и том же контуре две операции могут падать в разные стороны.

Trade-offs и режимы отказа. Централизованная служба решений даёт единые правила и становится зависимостью на горячем пути. Обычно её ответы кэшируют, и тогда отзыв прав срабатывает с задержкой. Гибкие модели усложняют ответ на вопрос «почему пользователю отказано», поэтому решение должно возвращать причину для отладки, но не раскрывать её пользователю.

Что мерить.

  • долю отказов авторизации;
  • число эндпоинтов без проверки на уровне объекта — оно должно быть нулевым и проверяться тестами;
  • задержку проверки;
  • время от изменения прав до вступления в силу.

Связано: SEC-03 OAuth · DATA-18 мультитенантность · SEC-12 аудит · SEC-06 безопасность API

Источники: OWASP API Security Top 10 — Broken Object Level Authorization · NIST — Attribute Based Access Control · Google Zanzibar: Consistent, Global Authorization System · OWASP — Authorization Cheat Sheet


SEC-05 · Хранение паролей#

Шаг W8

TL;DR. Пароль хранится только как результат намеренно медленной функции с уникальной солью. Всё остальное — вопрос выбора параметров.

Проблема. БД с паролями рано или поздно утекает — это надо принять как исходное условие. Задача хранения в том, чтобы утечка не давала злоумышленнику работающие пароли: ни напрямую, ни перебором по словарю, ни по заранее вычисленным таблицам (rainbow tables).

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

  • Соль (salt) — уникальное случайное значение для каждого пользователя, хранится рядом с хэшем. Делает бесполезными заранее вычисленные таблицы и скрывает совпадение одинаковых паролей у разных пользователей.
  • Медленная функция. Обычные хэши проектировались быстрыми, и как раз это делает их непригодными: современное оборудование перебирает миллиарды вариантов в секунду. Нужны функции с настраиваемой стоимостью: Argon2 (предпочтительный выбор), scrypt, bcrypt, PBKDF2. Первые две дополнительно требуют памяти, что обесценивает специализированное оборудование для перебора.
  • Параметры подбираются под своё железо: столько итераций и памяти, сколько допустимо по времени входа (ориентир — сотни миллисекунд). Их пересматривают со временем: параметры пятилетней давности уже слабые.
  • Прозрачное усиление. При входе, если хэш посчитан старыми параметрами, он пересчитывается новыми — обновление происходит без участия пользователя.
  • Перец (pepper) — общий секрет, который добавляется к паролю и хранится вне БД (в хранилище секретов). Утечка одной только БД становится бесполезной.
  • Сравнение за постоянное время (constant-time comparison) — чтобы по времени ответа нельзя было подбирать значение.

Смежные правила.

  • не ограничивать длину пароля жёстко сверху и не запрещать символы;
  • проверять по спискам утёкших паролей вместо требований к составу;
  • при неудачном входе не сообщать, существует ли такой пользователь;
  • замедлять и ограничивать попытки (EDGE-06);
  • восстанавливать пароль одноразовой ссылкой с коротким сроком, а не отправкой пароля.

Trade-offs и режимы отказа. Слишком дорогие параметры превращают вход в нагрузку и вектор DoS (отказа в обслуживании). Стоимость проверки должна учитываться в ёмкости. Собственная реализация схемы хранения — почти гарантированная ошибка. Кэширование результата проверки ради скорости сводит защиту на нет.

Что мерить.

  • время проверки пароля;
  • дату последнего пересмотра параметров;
  • долю учётных записей со старыми параметрами;
  • частоту неудачных входов.

Связано: SEC-01 аутентификация · SEC-08 секреты · EDGE-06 rate limiting

Источники: OWASP — Password Storage Cheat Sheet · RFC 9106 — Argon2 · NIST SP 800-63B — Digital Identity Guidelines · ByteByteGo — System Design: How to store passwords in the database?


SEC-06 · Безопасность API#

Шаг W8

TL;DR. У API свой профиль угроз: не столько инъекции в разметку, сколько отсутствующие проверки прав, избыточные ответы и незащищённые от объёма эндпоинты.

Проблема. API отличается от веб-страницы тем, что клиент полностью подконтролен злоумышленнику. Он видит все вызовы, может повторить любой с изменёнными параметрами и обращаться к эндпоинтам, которых нет в интерфейсе. Всё, что «спрятано» на клиенте, на деле не спрятано.

Чек-лист по угрозам, характерным для API.

  • Права на уровне объекта (BOLA, Broken Object Level Authorization) — самая частая и самая дорогая ошибка: смена идентификатора в запросе открывает чужие данные (SEC-04).
  • Права на уровне свойства. Mass assignment (массовое присвоение) полей из тела запроса позволяет установить role или is_admin; ответ, отдающий модель целиком, раскрывает внутренние поля. Обе стороны требуют явных списков разрешённых полей.
  • Расход ресурсов. Эндпоинт без ограничения размера страницы, глубины запроса или объёма тела — способ исчерпать память и БД одним вызовом (NET-15, EDGE-06).
  • Права на уровне функции. Административные эндпоинты, доступные обычному пользователю просто потому, что их нет в интерфейсе.
  • Неограниченный доступ к чувствительным потокам — восстановление пароля, отправка кодов, дорогие отчёты: требуют отдельных лимитов и наблюдения.
  • SSRF (подделка запросов на стороне сервера) — параметр с адресом, по которому сервер сам сходит (SEC-07).
  • Неучтённые версии и окружения. Старая версия API, тестовый стенд с данными прода, забытый отладочный эндпоинт. Инвентаризация поверхности не менее важна, чем защита известных её частей.

Практика. Валидация по схеме на входе (NET-18) отсекает целый класс проблем. Ответ формируется явным преобразованием, а не сериализацией доменной модели. Ошибки не раскрывают внутренние детали (NET-05). Секреты не попадают в адресную строку.

Trade-offs и режимы отказа. Жёсткая валидация ломает совместимость при добавлении полей. Поэтому правило «отвергать неизвестные поля» применяют к запросам, но не к ответам (NET-17). Единый перечень разрешённых полей нужно поддерживать, иначе он расходится с моделью, и новое поле утекает в ответ незамеченным.

Что мерить.

  • долю эндпоинтов, покрытых схемой и тестами на права;
  • число отказов авторизации по эндпоинтам;
  • распределение размеров ответов — выброс может означать утечку лишнего;
  • инвентарь активных версий API.

Связано: SEC-04 авторизация · SEC-07 типовые атаки · EDGE-07 защита периметра · NET-18 контракты

Источники: OWASP API Security Top 10 (2023) · OWASP — REST Security Cheat Sheet · ByteByteGo — Top 12 Tips For API Security


SEC-07 · Типовые атаки: инъекции, XSS, CSRF, SSRF#

Шаг W8

TL;DR. Четыре семейства с общим корнем: данные, пришедшие снаружи, интерпретируются как код, команда или адрес.

Проблема. Все механизмы предыдущих карточек защищают вход в систему. Эти атаки его не ломают — они используют легитимный доступ, чтобы заставить систему выполнить чужое намерение. Механику нужно понимать не ради списка: защита в каждом случае строится на одном принципе — разделять данные и управляющие конструкции, а не пытаться отфильтровать опасное.

Инъекции (SQL, NoSQL, команды ОС, LDAP). Данные пользователя попадают в текст запроса и меняют его структуру. Защита — параметризованные запросы, где данные передаются отдельно от текста и не могут стать его частью. Экранирование и чёрные списки — путь с постоянными обходами. Имена таблиц и колонок параметризовать нельзя, поэтому динамические части выбираются только из белого списка.

XSS (межсайтовый скриптинг). Данные пользователя попадают в разметку и исполняются как скрипт в браузере жертвы. Защита — контекстное экранирование при выводе (разметка, атрибут, JavaScript и URL требуют разного) и, лучше, шаблонизатор, экранирующий по умолчанию. CSP (Content Security Policy, политика безопасности содержимого) — второй эшелон, который ограничивает последствия. Успешный XSS обесценивает всё, что хранится в браузере, — отсюда флаг HttpOnly на cookie сессии (SEC-01).

CSRF (подделка межсайтового запроса). Чужая страница вынуждает браузер выполнить запрос с вашими cookie. Защита — SameSite у cookie как основа плюс токен, привязанный к сессии, для изменяющих операций. API на токенах в заголовке этой атаке не подвержено: заголовок не подставляется браузером автоматически (NET-19).

SSRF (подделка запроса на стороне сервера). Пользователь передаёт адрес, по которому сервер сам делает запрос, — и получает доступ во внутреннюю сеть, включая служебные интерфейсы облака. Так утекают ключи. Защита:

  • белый список адресов назначения;
  • запрет внутренних диапазонов после разрешения имени: проверку до разрешения обходят через DNS, указывающий на внутренний адрес;
  • отключение переходов по перенаправлениям;
  • выход через отдельный egress proxy (исходящий прокси, EDGE-02).

Общее правило. Проверка на входе полезна, но защита строится на выходе — в точке интерпретации: параметризованный запрос для БД, экранирование для разметки, разрешённый список для адреса. Валидация без разделения данных и кода лишь поднимает порог.

Trade-offs и режимы отказа. Валидация на входе полезна, но защита строится в точке интерпретации; попытка заменить одно другим даёт ложное чувство безопасности. Строгие фильтры ломают легитимные данные — текст с кавычками и SQL-подобными фрагментами в поле комментария. CSP ограничивает последствия XSS, но требует времени на внедрение и ломает встроенные скрипты.

Что мерить.

  • покрытие статическим анализом и проверкой зависимостей;
  • число мест с динамической сборкой запросов;
  • срабатывания CSP в режиме report-only;
  • долю исходящих запросов, идущих мимо egress proxy.

Связано: SEC-06 безопасность API · EDGE-07 защита периметра · NET-19 CORS · SEC-11 threat modeling

Источники: OWASP Top 10 · OWASP — SQL Injection Prevention Cheat Sheet · OWASP — Cross Site Scripting Prevention · OWASP — SSRF Prevention Cheat Sheet


SEC-08 · Шифрование, ключи и секреты#

Шаг W8

TL;DR. Шифрование данных — это на девяносто процентов управление ключами. Секрет в переменной окружения или в репозитории не защищён ничем.

Проблема. Требование «данные должны быть зашифрованы» звучит просто и часто выполняется формально: включено шифрование диска, а ключ лежит рядом. Реальные вопросы — от кого защищаемся, кто имеет доступ к ключу, как ключ меняется и что происходит при его компрометации.

Слои.

  • В канале (SEC-09) — обязательный минимум.
  • В покое (at rest) на уровне хранилища (шифрование диска или БД целиком) защищает от физического доступа к носителю и от неправильной утилизации. Не защищает от компрометации приложения: оно видит данные расшифрованными.
  • На уровне поля — шифруются отдельные значения, ключ хранится в отдельной системе. Защищает даже при полном доступе к БД, но ломает поиск и сортировку по этим полям, поэтому применяется точечно.
  • Иерархия ключей (envelope encryption). Данные шифруются ключом данных, ключ данных — главным ключом в защищённом хранилище. Ротация главного ключа не требует перешифрования всех данных — меняется только обёртка.

Секреты.

  • хранилище секретов с контролем доступа и журналом обращений;
  • выдача по идентичности сервиса (SEC-09), а не по общему паролю;
  • короткоживущие учётные данные вместо постоянных: динамически выданный доступ к БД на час лучше вечного пароля в конфигурации;
  • ротация по расписанию и по инциденту;
  • сканирование репозиториев на утечки.

Секрет, попавший в историю системы контроля версий, считается скомпрометированным — его меняют, а не удаляют коммит.

Криптографическая гигиена. Не изобретать схемы и не собирать их из примитивов: использовать проверенные библиотеки высокого уровня. Шифрование без проверки целостности небезопасно — нужны режимы AEAD (authenticated encryption), которые дают и то и другое. Одинаковые данные не должны давать одинаковый шифротекст, иначе он раскрывает структуру.

Уровни шифрования

Уровень От чего защищает От чего не защищает
В канале (TLS) Перехват и подмена в сети Компрометации любой из сторон
Диск или БД целиком Физического доступа к носителю, неверной утилизации Компрометации приложения — оно видит данные открытыми
Отдельные поля Доступа к БД, включая администратора БД Компрометации приложения, у которого есть ключ
Crypto-shredding Данных в старых бэкапах Копий, сделанных в расшифрованном виде: выгрузок, логов

Trade-offs и режимы отказа. Потеря ключа равна потере данных — отсюда требование к резервированию ключей, которое конфликтует с требованием их защищённости. Шифрование полей ломает поиск, сортировку, индексы и аналитику. Crypto-shredding требует управления ключом на каждого субъекта. Хранилище секретов становится критической зависимостью старта: сервисы кэшируют секреты, чтобы переживать его недоступность.

Что мерить.

  • возраст ключей и дату последней ротации;
  • число секретов вне хранилища;
  • долю сервисов с короткоживущими учётными данными;
  • срабатывания сканера утечек.

Связано: SEC-09 транспортная безопасность · SEC-05 пароли · DATA-19 retention · REL-09 бэкапы

Источники: OWASP — Cryptographic Storage Cheat Sheet · OWASP — Secrets Management Cheat Sheet · NIST SP 800-57 — Key Management


SEC-09 · Транспортная безопасность: TLS, mTLS, SSH#

Шаг W8

TL;DR. Внутренняя сеть перестала считаться доверенной. mTLS (взаимная проверка сертификатов) даёт сервисам проверяемую идентичность вместо «раз пришёл из нашей подсети — значит свой».

Проблема. Классическая модель защищала периметр: снаружи TLS, внутри открытый трафик и доверие по адресу. Она ломается, когда внутри работают сотни сервисов, часть инфраструктуры арендована, а компрометация одного узла даёт доступ ко всему. IP-адрес — не удостоверение: его можно занять.

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

  • TLS наружу (NET-04) — обязателен, вместе с запретом понижения протокола (downgrade) и автоматическим продлением сертификатов.
  • mTLS внутри. Обе стороны предъявляют сертификаты, поэтому сервис знает, кто к нему обратился, и решение о доступе принимается по идентичности, а не по адресу. Сертификат становится workload identity (удостоверением рабочей нагрузки).
  • Короткоживущие сертификаты. Час-сутки вместо года: отзыв в распределённой системе работает плохо, а короткий срок делает его почти ненужным. Автоматическая выдача при старте — обязательное условие, вручную это не эксплуатируется.
  • Zero trust (нулевое доверие) как принцип: каждый запрос аутентифицирован и авторизован независимо от того, откуда он пришёл; сеть не даёт привилегий.
  • SSH — тот же вопрос идентичности для людей. Сертификаты вместо разложенных по серверам открытых ключей, короткий срок, вход через bastion host (бастион) с журналированием, запрет входа под root и по паролю.
  • Где всё это живёт. Выдачу сертификатов, ротацию и правила доступа обычно берёт на себя инфраструктурный слой, например service mesh (ARCH-10). Иначе каждая команда реализует это по-своему.

Trade-offs и режимы отказа. mTLS добавляет TLS handshake (рукопожатия) и заметную нагрузку; помогает переиспользование соединений. Истёкший внутренний сертификат обрывает связь между сервисами, и это одна из самых частых аварий в таких схемах, поэтому нужен мониторинг сроков. Централизованный CA (удостоверяющий центр) — критическая зависимость: его недоступность блокирует запуск новых экземпляров.

Что мерить.

  • долю внутреннего трафика под mTLS;
  • срок до истечения сертификатов — минимальный по флоту;
  • число отказов рукопожатия;
  • долю SSH-доступов через бастион.

Связано: NET-04 TLS · SEC-08 ключи и секреты · ARCH-10 service mesh · EDGE-08 service discovery

Источники: NIST SP 800-207 — Zero Trust Architecture · SPIFFE — Secure Production Identity Framework · Google BeyondCorp · ByteByteGo — How SSH Really Works


SEC-10 · Безопасность цепочки поставок#

Шаг W9

TL;DR. Ваш код — малая часть того, что выполняется в проде. Остальное пришло из зависимостей, базовых образов и сборочного конвейера, и каждый из этих путей ведёт прямо в прод.

Проблема. Приложение подтягивает сотни пакетов, те — свои, и общее число редко кто знает. Компрометация одного популярного пакета, взлом учётной записи maintainer (сопровождающего) или подмена в процессе сборки дают злоумышленнику выполнение кода у всех потребителей сразу, минуя весь периметр. Это один из самых эффективных векторов атаки, и защита от него — процесс, а не настройка.

Что делать.

  • Знать состав. SBOM (Software Bill of Materials, ведомость компонентов) для каждой сборки. Без списка того, что внутри, невозможно ответить на вопрос «затронуты ли мы» при появлении уязвимости, а отвечать приходится в тот же день.
  • Фиксировать версии. Lock-файл с точными версиями и контрольными суммами. Сборка должна быть воспроизводимой (reproducible build). Плавающие версии означают, что в прод попадает то, чего никто не проверял.
  • Проверять зависимости сканером известных уязвимостей в конвейере, с политикой: что блокирует сборку, что заводит задачу. Отдельно — гигиена выбора: заброшенный пакет с одним сопровождающим и пятью строками кода не стоит риска.
  • Защитить сам конвейер. У него доступ к коду, секретам и проду, поэтому он ценная цель. Что помогает:
    • изолированные сборочные агенты;
    • минимальные права;
    • обязательное ревью изменений конфигурации конвейера;
    • запрет запуска произвольных скриптов из недоверенного источника, включая сборки по pull request от внешних участников.
  • Базовые образы — часть цепочки: минимальный образ из проверенного источника, регулярное обновление, фиксация по контрольной сумме, а не по подвижному тегу.
  • Подпись артефактов и проверка при выкатке. В прод попадает только то, что собрано вашим конвейером из вашего кода.
  • Typosquatting (опечатки в именах пакетов) — рабочая атака: имя, отличающееся одним символом, устанавливается вместо настоящего. Внутреннее зеркало реестра с белым списком закрывает и это, и внезапное исчезновение пакета.

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

Что мерить.

  • время от публикации уязвимости до выкатки исправления;
  • долю сборок с SBOM и подписью;
  • число прямых и транзитивных зависимостей;
  • возраст базовых образов;
  • долю пакетов из внутреннего зеркала.

Связано: OPS-01 CI/CD · SEC-11 threat modeling · OPS-06 конфигурация и IaC

Источники: SLSA — Supply-chain Levels for Software Artifacts · CISA/NTIA — Software Bill of Materials · OWASP — Dependency-Check и Top 10 CI/CD Security Risks · Sigstore


SEC-11 · Threat modeling#

Шаг W0

TL;DR. Threat modeling (моделирование угроз) — систематический способ найти угрозы до того, как их найдут за вас. Занимает час на систему и заменяет чек-лист, который неполон по своей природе.

Проблема. Безопасность обычно проверяют списком известных проблем. Список не покрывает то, что специфично для вашей системы: какие данные ценны, кто мотивирован, где границы доверия. Без явной модели решения принимаются по ощущению «это вроде важно», и защита оказывается неравномерной: где-то избыточной, где-то отсутствующей.

Как проводить.

  1. Что строим. Схема потоков данных из W4 с явно нарисованными границами доверия: браузер — сервер, сервис — сервис, ваш контур — внешняя система, обычный пользователь — администратор.
  2. Что может пойти не так. По каждому элементу и каждому пересечению границы задаются шесть вопросов схемы STRIDE. Процедура механическая — поэтому она находит то, что не приходит в голову. Вопросы:
    • Spoofing — подмена личности;
    • Tampering — изменение данных;
    • Repudiation — отказ от авторства;
    • Information disclosure — раскрытие информации;
    • Denial of service — отказ в обслуживании;
    • Elevation of privilege — повышение привилегий.
  3. Что с этим делать. По каждой угрозе: устранить, снизить, передать или принять — с явной записью решения.
  4. Проверить, что сделали. Угроза без проверки закрытия не считается закрытой.

Когда проводить. На этапе требований для нового сервиса; при изменении границ доверия (новая интеграция, новая роль, вынос компонента наружу); после инцидента. Пересматривать раз в год — модель устаревает вместе с системой.

Практические подсказки. Начинать с данных: где самое ценное, туда и смотреть в первую очередь. Считать злоумышленником не только внешнего: скомпрометированный сервис, недобросовестный сотрудник, ошибка соседней команды — те же угрозы. Один час с командой у доски даёт больше, чем длинный документ, который никто не прочитает.

Trade-offs и режимы отказа. Модель угроз легко превращается в ритуал — документ ради галочки. Признак живой модели: из неё выходят задачи в план работ. Полнота недостижима: цель — покрыть ценное и вероятное, а не всё возможное.

Что мерить.

  • долю сервисов с актуальной моделью;
  • число угроз, переведённых в задачи, и долю закрытых;
  • сколько инцидентов было предсказано моделью, а сколько оказалось за её пределами.

Связано: SEC-07 типовые атаки · SEC-12 аудит · REL-06 обзор отказоустойчивости · W0

Источники: OWASP — Threat Modeling Cheat Sheet · Microsoft — STRIDE и SDL Threat Modeling · Adam Shostack — Threat Modeling: Designing for Security


SEC-12 · Аудит и трассируемость действий#

Шаг W9

TL;DR. Журнал аудита (audit log) — запись того, кто, что и когда сделал с чувствительными данными. Нужен для расследования, для регуляторов и для ответа на вопрос «в какой момент это сломалось».

Проблема. Данные изменились неправильно. Кто это сделал — пользователь, интеграция, фоновая задача, миграция? Обычные логи приложения на этот вопрос не отвечают: они пишутся для отладки, ротируются через неделю, не содержат «до» и «после» и не защищены от изменения. Через месяц восстановить картину невозможно.

Чем аудит отличается от логов.

Логи приложения Журнал аудита
Назначение Отладка Расследование и отчётность
Содержание Что делал код Кто, над каким объектом, что изменил
Срок хранения Дни–недели Месяцы–годы (DATA-19)
Изменяемость Свободно Только добавление, изменение запрещено
Полнота По усмотрению разработчика По списку событий, обязательному к покрытию

Что записывать.

  • кто — пользователь или сервис, роль, от чьего имени действовал при делегировании;
  • что — действие, объект, значения до и после;
  • когда — время с зоной;
  • откуда — адрес, клиент, идентификатор запроса, тот же, что в трассировке (OPS-07);
  • результат, включая отказы: неудачные попытки доступа информативнее успешных.

Как хранить. Отдельно от БД прода, только на добавление, с правами, отличными от прав приложения: учётная запись сервиса не должна иметь возможности изменить журнал. Для повышенных требований — hash chain (цепочка контрольных сумм), которая делает незаметное изменение невозможным. Персональные данные в журнале подпадают под те же правила удаления, что и везде, — их либо не пишут, либо предусматривают обезличивание.

Trade-offs и режимы отказа. Аудит всего подряд стоит дорого и тонет в шуме, поэтому покрывают заранее определённый список значимых событий. Синхронная запись в журнал добавляет задержку и делает его доступность условием работы приложения. Асинхронная рискует потерять записи. Поэтому для критичных событий используют тот же приём, что и для доставки: запись в одной транзакции с изменением — transactional outbox (MSG-07). Журнал, который никто не читает и по которому нет проверок, обнаруживает свою неполноту в момент расследования.

Что мерить.

  • покрытие обязательных событий;
  • задержку появления записи;
  • число обращений к журналу при расследованиях;
  • долю событий с полным контекстом: кто, что, до/после;
  • соответствие срока хранения политике.

Связано: SEC-04 авторизация · OPS-07 observability · DATA-19 retention · DATA-16 целостность данных

Источники: OWASP — Logging Cheat Sheet · NIST SP 800-92 — Guide to Computer Security Log Management · Google SRE Book, гл. 26 «Data Integrity»


SEC-13 · Классификация данных и режим защиты#

Шаг W0

TL;DR. Какой класс данных что обязывает. Без перечня режим применяется либо ко всему подряд, либо ни к чему.

Проблема. Двенадцать карточек этого блока описывают механизмы — шифрование, аудит, токены, маскирование. Ни один из них не отвечает на вопрос, к чему их применять. В результате команда либо шифрует и аудирует всё, платя за это везде, либо не делает нигде и узнаёт о требовании от юриста. Обе крайности обходятся дороже перечня, который составляется за час.

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

Класс Примеры Что обязывает
Обычные Каталог, настройки, публичный контент Шифрование при передаче
Персональные Имя, телефон, адрес, идентификаторы устройства Плюс: шифрование на диске, аудит доступа, маскирование в логах и на стендах, срок хранения и процедура удаления (DATA-19)
Особо защищаемые Платёжные реквизиты, медицинские, биометрия Плюс: ограниченный круг лиц, отдельное хранилище, локализация, обязательный внешний аудит
Секреты Ключи, токены, пароли Не в коде и не в логах; хранилище секретов, ротация (SEC-08)

Класс влияет не только на хранение, но и на границы. Граница, которая рассекает класс, расселяет его по нескольким владельцам, и каждый должен держать режим целиком: стоимость умножается на число мест. Поэтому класс данных входит вопросом и дисквалификатором в решения о границах (D-02, D-05, D-07).

Когда применять. На W0, до проектирования границ и до выбора хранилищ. Когда нет: система не хранит данных людей и не обрабатывает платежи — тогда достаточно одного класса и строки об этом в требованиях.

Альтернативы

Подход Когда лучше Цена
Перечень классов с мерами По умолчанию Час работы и договорённость с юристом
Единый максимальный режим для всего Данных мало и все чувствительные Платится везде, в том числе там, где не нужно
Классификация по полям, а не по классам Сильно разнородные данные в одной таблице Перечень разрастается и перестаёт читаться

Trade-offs и режимы отказа. Классификация устаревает молча: поле добавили, класс не присвоили. Она же порождает ложную уверенность: «у нас есть политика». А данные всё равно утекают в логи и на тестовые стенды, куда их копируют без маскирования. Самый частый провал — не в проде, а на стендах и в выгрузках аналитики.

Что мерить.

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

Связано: SEC-08 шифрование, ключи и секреты · SEC-12 аудит и трассируемость · DATA-19 retention, удаление, приватность · ARCH-02 границы

Источники: GDPR, ст. 32 — безопасность обработки · GDPR, ст. 17 — право на стирание · NIST SP 800-60 — категоризация информации