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 (моделирование угроз) — систематический способ найти угрозы до того, как их найдут за вас. Занимает час на систему и заменяет чек-лист, который неполон по своей природе.
Проблема. Безопасность обычно проверяют списком известных проблем. Список не покрывает то, что специфично для вашей системы: какие данные ценны, кто мотивирован, где границы доверия. Без явной модели решения принимаются по ощущению «это вроде важно», и защита оказывается неравномерной: где-то избыточной, где-то отсутствующей.
Как проводить.
- Что строим. Схема потоков данных из W4 с явно нарисованными границами доверия: браузер — сервер, сервис — сервис, ваш контур — внешняя система, обычный пользователь — администратор.
- Что может пойти не так. По каждому элементу и каждому пересечению границы задаются шесть вопросов схемы STRIDE. Процедура механическая — поэтому она находит то, что не приходит в голову. Вопросы:
- Spoofing — подмена личности;
- Tampering — изменение данных;
- Repudiation — отказ от авторства;
- Information disclosure — раскрытие информации;
- Denial of service — отказ в обслуживании;
- Elevation of privilege — повышение привилегий.
- Что с этим делать. По каждой угрозе: устранить, снизить, передать или принять — с явной записью решения.
- Проверить, что сделали. Угроза без проверки закрытия не считается закрытой.
Когда проводить. На этапе требований для нового сервиса; при изменении границ доверия (новая интеграция, новая роль, вынос компонента наружу); после инцидента. Пересматривать раз в год — модель устаревает вместе с системой.
Практические подсказки. Начинать с данных: где самое ценное, туда и смотреть в первую очередь. Считать злоумышленником не только внешнего: скомпрометированный сервис, недобросовестный сотрудник, ошибка соседней команды — те же угрозы. Один час с командой у доски даёт больше, чем длинный документ, который никто не прочитает.
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 — категоризация информации