Безопасность приложений: карта трека и как думать об угрозах
Этот трек — про защиту. Мы разбираем механику уязвимостей ровно настолько, насколько это нужно, чтобы находить их в своём коде, чинить и не допускать повторно. Здесь нет готовых боевых эксплойтов под конкретные живые системы и нет инструкций «как проникнуть куда-то ещё»: такой материал бесполезен разработчику и незаконен вне рамок авторизованной работы.
Рамка, которая действует на весь трек. Любая практическая проверка защищённости — сканирование, фаззинг, попытка обойти авторизацию — выполняется только:
- на вашей собственной системе или на системе, владелец которой дал письменное разрешение с явно описанным периметром (scope), окном времени и контактом для экстренной остановки;
- в рамках официальной программы (bug bounty, VDP) — строго по её правилам и в её границах;
- на намеренно уязвимых учебных стендах, поднятых локально: OWASP Juice Shop, WebGoat, DVWA.
Устная договорённость «шеф разрешил посмотреть» разрешением не является. В России несанкционированный доступ к компьютерной информации и связанные с ним действия квалифицируются по статьям 272–274 УК РФ — это факт законодательства, а не юридическая консультация; за интерпретацией идите к юристу, а за разрешением — к владельцу системы, письменно.
1. Чем безопасность отличается от корректности
Функциональное требование говорит, что система должна делать. Требование безопасности говорит, чего система не должна делать никогда, включая случаи, когда вход подобран специально.
Классическая формулировка разницы принадлежит Брюсу Шнайеру: обычный инженер спрашивает «как это заставить работать», инженер по безопасности — «как это заставить сломаться». Тестировщик проверяет конечное множество сценариев, которые кто-то придумал. Атакующий работает в бесконечном дополнении к этому множеству: он не обязан ходить по вашим экранам, он отправляет HTTP-запрос напрямую, меняет порядок шагов мастера, подставляет чужой идентификатор, присылает JSON с полем, которого нет в форме.
| Функциональный баг | Уязвимость | |
|---|---|---|
| Кто находит | пользователь случайно | нарушитель намеренно |
| Вход | «разумный» | подобранный, враждебный |
| Частота | вылезает сама | может не проявиться годами |
| Цена ожидания | падает со временем | растёт: данные утекают тихо |
| Тест | позитивный сценарий | негативный: «а вот так нельзя» |
| Обнаружение | по жалобам | по логам, если они есть |
Отсюда первое практическое следствие: на каждую починенную уязвимость пишется негативный тест, который навсегда фиксирует запрет. Как встраивать такие тесты в общую стратегию — в треке «Тестирование».
Свойства, которые мы защищаем, традиционно сводят к триаде CIA и двум дополнениям:
- Confidentiality — данные видит только тот, кому положено;
- Integrity — данные меняет только тот, кому положено, и только предусмотренным способом;
- Availability — система отвечает тем, кому должна;
- Authenticity — субъект действительно тот, за кого себя выдаёт;
- Non-repudiation — от совершённого действия нельзя отпереться (аудит, подписи).
Формально эти категории закреплены в NIST FIPS 199 и семействе ISO/IEC 27000. Их полезность не в терминологии, а в вопросе к каждой фиче: какое из пяти свойств здесь можно нарушить и что это будет стоить?
2. Границы доверия и поверхность атаки
Основное понятие всего трека — граница доверия (trust boundary): место, где данные или управление переходят из зоны с одним уровнем доверия в зону с другим. Поверхность атаки — сумма всех таких переходов.
Три правила, которые из этой картинки следуют буквально.
Правило 1. Всё, что пришло из-за границы, — недоверенное. Не «поле формы», а весь запрос: путь, query, заголовки, cookie, тело, имена и содержимое файлов, Content-Type, порядок и время вызовов. Валидация на клиенте — это UX, а не защита: клиент исполняется на машине, полностью подконтрольной пользователю, и HTTP-запрос можно отправить мимо него.
Правило 2. Проверка живёт на той стороне границы, которой вы управляете. Если проверку можно снести, отредактировав JavaScript в браузере или подменив заголовок, её нет.
Правило 3. Обратный поток тоже пересекает границу. Текст ошибки, лишнее поле в JSON-ответе, стек-трейс, PII в логах и метриках, различие «404 или 403» — всё это утечки. Ответ проектируется так же осознанно, как запрос.
Граница №4 на схеме — самая недооценённая. Ваша сборка выполняет чужой код: пакеты, базовые образы, действия CI. Он получает те же права, что и вы, — этому посвящена статья «Безопасность цепочки поставок».
3. Цикл мышления об угрозах
Мышление об угрозах — не озарение, а повторяемая процедура из четырёх вопросов Threat Modeling Manifesto: что мы строим → что может пойти не так → что с этим делаем → достаточно ли хорошо сделали.
деньги, PII, доступ, репутация"] --> B["Поток: как данные
попадают к активу"] B --> C["Граница доверия:
где меняется уровень доверия"] C --> D["Нарушитель: кто он
и что у него уже есть"] D --> E{"Что он может
сделать на границе?"} E -->|"подделать личность"| F["Контроль:
аутентификация"] E -->|"взять чужой объект"| G["Контроль:
авторизация на объекте"] E -->|"подсунуть данные
как код"| H["Контроль:
параметризация, кодирование"] E -->|"перегрузить"| I["Контроль:
лимиты, квоты, таймауты"] F --> J{"Контроль есть
и он проверен тестом?"} G --> J H --> J I --> J J -->|"нет"| K["Находка: заводим задачу
с описанием сценария"] J -->|"да"| L["Регрессионный тест
в CI"] K --> M["Приоритизация:
влияние x лёгкость"] M --> N["Исправление + тест"] N --> L L --> O["Изменилась архитектура?
цикл повторяется"] O --> A
Ключевой узел здесь — «кто нарушитель и что у него уже есть». Без модели нарушителя разговор скатывается в «а вдруг придёт АНБ», и защита либо парализуется, либо становится театром.
| Нарушитель | Что у него уже есть | Что должно его останавливать |
|---|---|---|
| Аноним из интернета | сеть, время, скрипты | аутентификация, лимиты, отсутствие лишних эндпоинтов |
| Массовый сканер / ботнет | базы утёкших паролей, эксплойты под старые версии | обновления, MFA, ограничение частоты |
| Легальный пользователь | валидная сессия | авторизация на каждом объекте, серверная валидация бизнес-правил |
| Пользователь другого арендатора | валидная сессия в SaaS | изоляция арендаторов, tenant_id в каждом запросе, RLS |
| Скомпрометированная зависимость | исполнение в вашем процессе | пиннинг версий, SBOM, минимум прав у сборки |
| Утёкший CI-токен | право деплоя | короткоживущие OIDC-токены, ограничение окружений, аудит |
| Инсайдер с админ-панелью | легальный доступ | минимум привилегий, неизменяемый аудит, разделение обязанностей |
Обратите внимание: половина строк — это не «хакер», а легальный субъект, вышедший за свои границы. Именно поэтому первая по частоте категория находок в реальных приложениях — сломанный контроль доступа, а не экзотические переполнения.
4. Словарь: CWE, CVE, CVSS, EPSS, KEV
Эти аббревиатуры путают постоянно, а они относятся к разным сущностям.
- CWE — каталог классов слабостей. CWE-89 «SQL Injection», CWE-79 «XSS», CWE-639 «Authorization Bypass Through User-Controlled Key». Это язык, на котором описывают вид ошибки в вашем коде. Ориентир — CWE Top 25.
- CVE — идентификатор конкретной уязвимости в конкретном продукте и версии. CVE-2021-44228 (Log4Shell) — экземпляр класса CWE-917 в Apache Log4j. У вашего собственного кода CVE обычно нет — у него есть находки, классифицируемые по CWE.
- CVSS — метрика серьёзности, 0–10. Важно: базовый CVSS описывает уязвимость «в вакууме» и не является риском. Дыра с CVSS 9.8 в библиотеке, которую вы не вызываете, менее опасна, чем IDOR с CVSS 6.5 в эндпоинте счетов.
- EPSS — вероятность, что уязвимость будут эксплуатировать в ближайшие 30 дней. Отлично работает как фильтр очереди на патчинг.
- KEV — каталог CISA уязвимостей, эксплуатация которых уже наблюдалась. Попадание в KEV = чинить сейчас, а не в следующем спринте.
Риск, в отличие от всего перечисленного, считается у вас: риск ≈ вероятность × ущерб, где вероятность зависит от вашей экспозиции (доступен ли эндпоинт из интернета, нужна ли аутентификация), а ущерб — от актива. Отсюда практика приоритизации:
Правый верхний квадрант закрывается сегодня. Правый нижний — дешёвая гигиена, делается пачкой. Левый верхний — планируется как инженерная работа. Левый нижний — документируется как принятый риск, с датой пересмотра.
5. Восемь принципов, которым пятьдесят лет
В 1975 году Джером Зальцер и Майкл Шрёдер опубликовали «The Protection of Information in Computer Systems». Их принципы старше веба и до сих пор объясняют большинство инцидентов.
- Economy of mechanism — механизм защиты должен быть простым настолько, чтобы его можно было проверить глазами. Своя «умная» схема сессий с четырьмя видами токенов не проверяема, а значит, дырява.
- Fail-safe defaults — по умолчанию запрещено. Права выдаются явно. Новый эндпоинт без явного правила доступа должен возвращать 403, а не 200.
- Complete mediation — проверка на каждом обращении, а не один раз при входе в раздел. Кешированное «этот пользователь админ» переживает отзыв прав.
- Open design — стойкость держится на ключе, а не на секретности алгоритма (принцип Керкгоффса). Если система ломается от того, что кто-то прочитал ваш код, она уже сломана.
- Separation of privilege — для опасного действия нужны два независимых условия: пароль и второй фактор, подпись и ревью.
- Least privilege — у каждого субъекта минимум прав на минимальное время. Сервису отчётов не нужен доступ на запись, а CI-джобе — постоянный ключ вместо короткоживущего.
- Least common mechanism — меньше общего разделяемого состояния между субъектами: общий кеш без ключа арендатора однажды отдаст чужие данные.
- Psychological acceptability — защита, которая мешает работать, будет обойдена людьми. Требование менять пароль каждые 30 дней даёт
Password_2026_07!на стикере — поэтому NIST SP 800-63B от него и отказался.
К ним добавились три современных:
- Defense in depth — несколько независимых слоёв, ни один не считается непробиваемым;
- Secure by default — безопасная конфигурация «из коробки», небезопасное — включается осознанно;
- Assume breach — исходим из того, что периметр уже пробит; отсюда сегментация, короткоживущие секреты, аудит и модель Zero Trust (NIST SP 800-207).
Смысл картинки — арифметический. Если один контроль ловит 90 % попыток, три независимых слоя оставляют 0,1 %. Слово «независимых» ключевое: три проверки, читающие один и тот же непроверенный заголовок, — это один слой, повторённый трижды.
6. Путь запроса через контроли
Порядок проверок — не стилистика, а требование безопасности: дорогая работа не должна выполняться до дешёвых отказов, а данные не должны попадать в обработчик раньше валидации.
аудит пишется на каждом отказе, иначе перебор невидим
Три детали, которые часто теряют. Первая: аутентификация раньше валидации, валидация раньше бизнес-логики — иначе анонимный запрос заставляет систему разбирать сложный JSON. Вторая: авторизация проверяет доступ к объекту, а не к маршруту; проще всего это гарантировать, вшив ограничение в сам запрос к данным. Третья: отказы обязаны попадать в аудит — без записей о неудачных попытках перебор идентификаторов выглядит как тишина.
7. Схема разбора уязвимости, по которой построен весь трек
Каждую уязвимость в треке мы разбираем одинаково: как выглядит уязвимый код → почему это работает → как чинить правильно → как проверить, что починено. Покажем схему на примере самого частого класса — доступа к чужому объекту по прямой ссылке (CWE-639, CWE-284; в OWASP API Security Top 10 это API1:2023 Broken Object Level Authorization).
7.1. Как выглядит уязвимый код
from fastapi import FastAPI, Depends
app = FastAPI()
@app.get("/api/invoices/{invoice_id}")
async def get_invoice(invoice_id: int, user=Depends(current_user)):
# Пользователь аутентифицирован — значит, можно? Нет.
# Идентификатор пришёл от клиента и никак не связан с этим пользователем.
row = await db.fetch_one(
"SELECT * FROM invoices WHERE id = :id", {"id": invoice_id}
)
return row # плюс отдаём все колонки, включая внутренние
Код проходит ревью на автомате: инъекции нет (запрос параметризован), аутентификация есть, тесты зелёные — владелец счёта видит свой счёт.
7.2. Почему это работает
Аутентификация отвечает на вопрос «кто ты», авторизация — «что тебе можно». Здесь есть первое и нет второго. Идентификатор объекта — последовательное целое, то есть предсказуемый; соседний счёт лежит по соседнему числу. Никакой ошибки не происходит: приложение делает ровно то, что написано, — отдаёт запрошенную строку любому, кто предъявил хоть какую-нибудь валидную сессию. Дополнительная утечка — SELECT *: в ответ уезжают внутренние поля (себестоимость, флаги, комментарии), даже если фронтенд их не показывает.
Замена целых идентификаторов на UUID не является исправлением: она снижает вероятность угадать, но ссылка попадает в историю браузера, в реферер, в скриншот и в тикет поддержки. Это обфускация, а не контроль доступа.
7.3. Как чинить правильно
Правильное исправление делает нарушение невыразимым: ограничение по владельцу вшито в сам запрос, а не приписано отдельной проверкой, которую можно забыть.
from fastapi import FastAPI, Depends, HTTPException
from pydantic import BaseModel
class InvoiceOut(BaseModel):
"""Явный контракт ответа: наружу уходит только перечисленное."""
id: int
number: str
amount: str
currency: str
issued_at: str
@app.get("/api/invoices/{invoice_id}", response_model=InvoiceOut)
async def get_invoice(invoice_id: int, user=Depends(current_user)):
row = await db.fetch_one(
"""
SELECT id, number, amount, currency, issued_at
FROM invoices
WHERE id = :id AND tenant_id = :tenant
""",
{"id": invoice_id, "tenant": user.tenant_id}, # ограничение — часть запроса
)
if row is None:
# 404, а не 403: ответ не подтверждает существование чужого объекта
raise HTTPException(status_code=404, detail="Не найдено")
return row
Второй эшелон — на уровне СУБД. Row-Level Security в PostgreSQL превращает изоляцию арендаторов в инвариант хранилища: даже забытый WHERE не выдаст чужие строки.
-- Включаем RLS и запрещаем всё, что не разрешено явно.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON invoices
USING (tenant_id = current_setting('app.tenant_id', true)::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id', true)::uuid);
-- Приложение выставляет арендатора один раз на транзакцию:
-- SET LOCAL app.tenant_id = '...';
-- SET LOCAL, а не SET: значение не переживёт возврат соединения в пул.
Обратите внимание на WITH CHECK: без него политика ограничивает чтение, но позволяет вставить строку с чужим tenant_id. Подробности изоляции арендаторов — в статье «Авторизация», особенности транзакций и пулов соединений — в треке «Базы данных».
7.4. Как проверить, что починено
Проверка — это тест, который падал бы на уязвимой версии. Он живёт в CI навсегда.
import pytest
@pytest.mark.parametrize("method,path_tpl", [
("get", "/api/invoices/{id}"),
("patch", "/api/invoices/{id}"),
("delete", "/api/invoices/{id}"),
])
def test_чужой_счёт_недоступен(client, alice_token, bob_invoice_id, method, path_tpl):
"""Алиса аутентифицирована, но объект принадлежит Бобу.
Ожидаем 404: не подтверждаем даже факт существования объекта.
Тест обязан покрывать ВСЕ методы маршрута — правку и удаление забывают чаще всего.
"""
response = getattr(client, method)(
path_tpl.format(id=bob_invoice_id),
headers={"Authorization": f"Bearer {alice_token}"},
)
assert response.status_code == 404
assert "amount" not in response.text # ничего не утекло и в теле ошибки
def test_ответ_не_содержит_внутренних_полей(client, alice_token, own_invoice_id):
body = client.get(f"/api/invoices/{own_invoice_id}",
headers={"Authorization": f"Bearer {alice_token}"}).json()
assert set(body) == {"id", "number", "amount", "currency", "issued_at"}
Дополнительно к юнит-тестам работает инвариантная проверка: пройти по списку маршрутов приложения и убедиться, что у каждого есть явная политика доступа. Такой тест ловит новый эндпоинт, добавленный без проверки прав, — то есть регрессию, которую точечные тесты не увидят.
Эта же четырёхшаговая схема применяется дальше ко всем классам: инъекциям, XSS, ошибкам OAuth, слабым JWT, дырам в загрузке файлов.
8. Жизненный цикл находки
Находка — это не «сообщение в чате», а объект с состоянием и сроками. Процесс описан в ISO/IEC 30111 (обработка) и ISO/IEC 29147 (раскрытие).
дата пересмотра обязательна S4 --> S6 : назначен владелец и SLA S5 --> S6 : пересмотр показал рост риска S6 --> S7 : исправление + негативный тест S7 --> S8 : независимая проверка,
тест падает на старой версии S8 --> S9 : advisory, CVE,
уведомление пользователей S8 --> [*] S9 --> [*]
Типовые SLA, от которых удобно отталкиваться: критично — 24–72 часа, высоко — до 7 дней, средне — до 30, низко — до 90. Числа настраиваются под продукт, важен сам факт часов на счётчике: без срока «подтверждена» превращается в «навсегда». И отдельно: «принятый риск» — это решение владельца продукта с датой пересмотра, а не молчание инженера, который не успел починить.
Если у вас есть внешние пользователи, заведите канал приёма сообщений и опишите его в файле /.well-known/security.txt — формат стандартизован RFC 9116:
# Файл отдаётся по HTTPS и содержит минимум четыре поля:
curl -s https://example.com/.well-known/security.txt
# Contact: mailto:security@example.com — куда писать
# Expires: 2027-01-01T00:00:00.000Z — обязательное поле, иначе файл невалиден
# Preferred-Languages: ru, en
# Policy: https://example.com/security-policy
Без такого канала исследователь, нашедший дыру, пишет в поддержку, получает шаблонный ответ и уходит публиковать находку. Это не гипотетический сценарий, а самая частая причина «внезапных» публичных раскрытий.
9. Карта трека
- 01. Моделирование угроз — STRIDE, диаграммы потоков данных, поверхность атаки, полный разбор на примере сервиса.
- 02. OWASP Top 10 — что это за документ, как его читать и что конкретно делать с каждой категорией.
- 03. Инъекции — SQL, NoSQL, команды ОС, шаблонизаторы: одна общая механика «данные стали кодом» и общая защита.
- 04. XSS, CSRF и клиентские атаки — типы XSS, контекстное кодирование, CSP,
SameSite, защита форм. - 05. Аутентификация — хранение паролей, Argon2 и bcrypt, сессии, MFA, безопасное восстановление доступа.
- 06. OAuth 2.0 и OpenID Connect — потоки, PKCE,
state,nonce, типичные ошибки внедрения. - 07. JWT и токены — устройство, подпись, срок жизни, отзыв и честный ответ на вопрос «а нужен ли вам JWT».
- 08. Авторизация — RBAC, ABAC, ACL, изоляция арендаторов и место проверки прав в коде.
- 09. Прикладная криптография — симметрика, асимметрика, хеши, подписи, KDF и правило «не изобретать своё».
- 10. Транспортная безопасность — TLS, сертификаты, mTLS, пиннинг, HSTS.
- 11. Безопасность API — IDOR, массовое присвоение, валидация, ограничение частоты, версионирование.
- 12. Секреты и ключи — переменные окружения, Vault, ротация, что делать с утёкшим ключом.
- 13. Цепочка поставок — зависимости, SBOM, подпись артефактов, защита CI.
- 14. Безопасная разработка — ревью, SAST/DAST/SCA, security-чемпионы, встраивание в SDLC.
- 15. Приватность и соответствие — персональные данные, минимизация, сроки хранения, аудит.
Соседние треки, к которым мы будем обращаться: DevOps — безопасность конвейера сборки; Тестирование — как тестировать защищённость; Операционные системы — изоляция процессов и контейнеров; Основы CS — базовые понятия с нуля; AI-инженерия — prompt injection как новый класс той же старой проблемы «данные стали инструкцией»; Web3 — безопасность смарт-контрактов. Инженерные практики работы с персональными данными в российском правовом поле разобраны в отдельном материале портала о 152-ФЗ и privacy engineering — там же ссылки на первоисточники; юридических консультаций мы не даём.
10. Минимальный набор, который окупается всегда
Если завтра нужно поднять планку защищённости в существующем сервисе, порядок действий такой — от дешёвого и универсального к дорогому и специфичному.
# 1. Секреты в истории репозитория — ищем и отзываем найденное.
# Утёкший ключ считается скомпрометированным навсегда: правка коммита его не отменяет.
gitleaks detect --source . --redact
# 2. Известные уязвимости в зависимостях: сравнение манифестов с базами OSV/NVD.
osv-scanner scan source . # универсально, читает lock-файлы
npm audit --omit=dev # Node.js, только прод-зависимости
govulncheck ./... # Go: проверяет достижимость уязвимого кода
# 3. Уязвимости в базовом образе — их обычно больше, чем в вашем коде.
trivy image --severity HIGH,CRITICAL registry.example.com/app:1.4.2
# 4. Статический анализ на классы CWE.
semgrep --config auto .
# 5. Заголовки безопасности и конфигурация TLS — только для СВОЕГО домена.
curl -sI https://app.example.com | grep -iE 'strict-transport|content-security|x-content-type'
Дальше — то, что делается один раз и работает годами: включить MFA для админов и CI; убрать долгоживущие ключи в пользу короткоживущих OIDC-токенов; включить автоматическое обновление зависимостей с прогоном тестов; вынести опасные операции за отдельную проверку прав; настроить аудит-лог отказов и алерт на всплеск 401/403; описать security.txt. Ни один из пунктов не требует отдельного бюджета, а вместе они закрывают большую часть массовых атак.
11. Типичные ошибки мышления
- «У нас нет ничего интересного». Массовые атаки не выбирают жертву: сканируют весь диапазон адресов. Ценность есть всегда — вычислительные ресурсы, почтовая репутация домена, база email для рассылок, доступ к вашим клиентам через вас.
- «Мы за VPN, значит, в безопасности». Периметр даёт один слой. Скомпрометированный ноутбук сотрудника или уязвимость в самом VPN-шлюзе делают внутреннюю сеть внешней — это и есть предпосылка Zero Trust.
- «У нас же есть WAF». WAF фильтрует известные шаблоны. Он не знает, что счёт №42 принадлежит Бобу, и не заменяет ни авторизацию, ни параметризацию.
- «Мы всё шифруем». Шифрование диска не спасает от SQL-инъекции: приложение имеет права на расшифрованное чтение, и злоупотребление идёт через него. Формулируйте, от какого нарушителя защищает конкретная мера.
- «Пентест раз в год закроет вопрос». Отчёт описывает состояние на дату; за год выкатывается сотня релизов. Пентест — проверка процесса, а не замена ему.
- «Безопасность добавим после запуска». Стоимость исправления растёт на порядки от дизайна к продакшену — базовое наблюдение из NIST SSDF (SP 800-218) и всей практики shift-left. Смена схемы аутентификации после релиза — это миграция всех пользователей.
- «Свой алгоритм безопаснее, никто не знает, как он устроен». Принцип Керкгоффса и открытый дизайн: криптографию, сессии и контроль доступа берут готовыми и проверенными.
- «Проверили на клиенте». Валидация в браузере и мобильном приложении — про удобство. Запрос отправляется мимо неё.
- «Тесты зелёные, значит, безопасно». Тесты проверяют то, что должно работать. Уязвимость — это то, что не должно работать, но работает; для этого нужны негативные тесты и моделирование угроз.
- «Обновимся, когда будет время». Между публикацией advisory и массовой эксплуатацией часто проходят часы. Автообновление зависимостей — не гигиена, а контроль.
12. Мини-итог
- Безопасность — это требование о том, чего система не должна делать никогда, включая враждебный вход; проверяется негативными тестами.
- Единица анализа — граница доверия; сумма границ и есть поверхность атаки, и обратный поток данных пересекает её так же, как прямой.
- Модель нарушителя обязательна: без ответа «кто он и что у него уже есть» защита превращается в театр. Половина реальных нарушителей — легальные пользователи, вышедшие за свои границы.
- CWE — класс слабости, CVE — экземпляр в продукте, CVSS — серьёзность в вакууме, EPSS и KEV — про реальную эксплуатацию. Риск считаете вы, в своём контексте.
- Принципы Зальцера и Шрёдера объясняют большинство инцидентов: запрет по умолчанию, проверка на каждом обращении, минимум привилегий, простота механизма.
- Эшелонированная оборона работает арифметически — при условии, что слои независимы.
- Каждая уязвимость разбирается по схеме: уязвимый код → механика → исправление (желательно делающее нарушение невыразимым) → тест, который падал бы на старой версии.
- Любая практическая проверка чужой системы — только с письменного разрешения владельца и в описанных границах; для тренировки есть локальные уязвимые стенды.
Источники
Документы, к которым трек возвращается постоянно:
- OWASP: Top 10, API Security Top 10, ASVS — стандарт требований по уровням L1–L3, Cheat Sheet Series, SAMM — модель зрелости процесса.
- MITRE: CWE, CWE Top 25, ATT&CK — каталог техник нарушителей, полезен как источник сценариев для моделирования.
- NIST: SSDF SP 800-218 — практики безопасной разработки; SP 800-63B — требования к аутентификации и паролям; SP 800-207 — Zero Trust; FIPS 199 — категоризация по CIA.
- FIRST: CVSS v4.0, EPSS. CISA: KEV.
- RFC: 9116 —
security.txt; 6749 и 9700 — OAuth 2.0 и современные best practices; 7519 — JWT; 8446 — TLS 1.3. - ISO/IEC 29147 — раскрытие уязвимостей, 30111 — обработка сообщений о них.
Книги: Adam Shostack, Threat Modeling: Designing for Security (Wiley, 2014) — базовая книга по STRIDE; Ross Anderson, Security Engineering, 3-е издание — главы доступны бесплатно; Michal Zalewski, The Tangled Web (No Starch Press, 2011) — устройство браузерной модели безопасности; David Wong, Real-World Cryptography (Manning, 2021) — прикладная криптография без математического ада; Alice and Bob Learn Application Security (Wiley, 2020) — мягкий вход для разработчиков.
Разборы инцидентов как учебный материал (все опубликованы официально и разбираются на уровне причин, а не техник): отчёт Конгресса США по Equifax — необновлённая библиотека и отсутствие сегментации; отчёт CSRB по Log4Shell — цена невидимости зависимостей; решение суда и постмортемы по инциденту Capital One — чрезмерные права роли в облаке.
Что дальше
Моделирование угроз: STRIDE, поверхность атаки, разбор на примере — превратим интуицию из этой статьи в процедуру: нарисуем диаграмму потоков данных реального сервиса, разметим границы доверия, пройдёмся по шести категориям STRIDE и получим на выходе список конкретных находок с приоритетами и владельцами. Это тот артефакт, который дальше превращается в задачи в трекере и в тесты в CI.