OWASP Top 10 (2021) — concept page covering all 10 vulnerability classes with 3 deep-dive scenarios: A03 SQL injection (vulnerable string-concat vs parameterized queries), A01 Broken Access Control (IDOR — First American 2019 style), A10 SSRF to AWS IMDS leading to IAM credential theft (Capital One 2019). Topology: Attacker -> WAF/LB (edge) -> API + AuthZ + Audit Logger (app) -> Postgres + Secrets Vault + AWS IMDS + S3 (internal). 2 ADRs on the WAF node: (1) defense-in-depth — где какая защита, mapping each OWASP item to the right layer (WAF / API Gateway / App / Infra / Supply chain / Observability), (2) A03 SQL injection — prepared statements vs ORM vs string concat with concrete code rules and gotchas (LIKE, IN, ORDER BY identifiers).
OWASP Top 10 — это не «свежие хакерские техники», а список 10 классов уязвимостей, которые встречаются в 95%+ реальных breaches последние двадцать лет. Когда у Equifax утекли 147M записей, у Capital One — 100M кредитных заявок, у First American Financial — 885M ипотечных документов, у TalkTalk — 157K customer records, — все эти инциденты сводятся к одному-двум пунктам из этой десятки. Не к нулевым дням, не к state-level APT, не к социальной инженерии. К class-of-bugs, который существует с 1999 года и который любой пентестер первым делом проверяет за 10 минут с burp suite или sqlmap.
Top 10 живёт по простой логике: если ты строишь web/API систему и не закрыл эти 10 классов, ты гарантированно уязвим — не потому что атакующий умнее тебя, а потому что в твоей системе есть давно известные дыры. CIO/CTO часто думают «нас защитит WAF» или «у нас сложная архитектура — нас не сломают» — практика говорит обратное: WAF ловит ~30% (signature-based, видит только known payload patterns), а сложная архитектура расширяет attack surface. Реальная защита — это понять каждый из 10 классов, понять на каком слое он закрывается, и закрыть его осознанно. Это не security-feature, это санитария.
OWASP Top 10 — также общий язык. Когда compliance-аудит (PCI DSS, SOC 2, HIPAA, GDPR) спрашивает «как вы защищены от injection» — он спрашивает про A03. Когда threat modeling (см. ::concept{slug="threat-modeling-stride"}) маппится на конкретные categories — это маппинг STRIDE → OWASP. Без знания этого списка ты будешь «изобретать security заново» каждый раз и пропустишь очевидное.
«OWASP Top 10 — это checklist того, что атакующий проверит первым делом. Не "продвинутые APT", а 10 классов багов, которые лежат в коде десятилетиями. Если хотя бы один открыт — pwned за 10 минут с готовыми tools. Defense — не magic, а defense-in-depth по слоям: WAF (~30%), API gateway, app code, infra, supply chain, observability — каждый закрывает свой набор пунктов.»
Ключ к Top 10 — не запомнить список, а понять структуру:
Заметь: только A01–A03 — «классические application bugs». Остальные 7 — про процессы, инфраструктуру и культуру. Если твоя security-стратегия — только pen-test раз в год, ты закрываешь дай бог 30% поверхности.
Канвас показывает реалистичный production-deployment с пятью trust zones, разделёнными чёткими границами:
bot — атакующий с burp suite/sqlmap. Снаружи всех trust boundaries.waf (Cloudflare/ModSecurity) и lb (Load Balancer). Первая линия защиты: signature-based фильтры, rate limiting, TLS termination.api (API server), authz (AuthZ middleware), logger (Audit logger → SIEM). Бизнес-логика, validation, ownership checks.db (Postgres с orders/users), secrets (Vault), metadata (AWS IMDS на 169.254.169.254 — главный SSRF-target), s3 (PII bucket).Edges — физические соединения: bot → waf → lb → api, далее api → db/secrets/metadata/s3. Ноды metadata и s3 — это то, что атакующий хочет (creds + data). WAF на waf несёт ADR-001 (defense-in-depth: где какая защита) и ADR-002 (A03 SQL injection — prepared statements vs ORM vs concat). Это не теория — это паттерны, которые работают в проде.
1. A03 SQL Injection (vulnerable vs parameterized). Атакующий шлёт password=' OR '1'='1 в login endpoint. WAF может пропустить (obfuscation 0x27%20OR%201%3D1 обходит regex). Если код делает string concat "SELECT * FROM users WHERE name='" + user + "' AND pass='" + pass + "'", БД возвращает admin row → auth bypass → admin session token. Сценарий показывает escalation (DROP TABLE users-- следующим шагом) и fix: parameterized query db.query("SELECT * FROM users WHERE name=$1 AND pass=$2", [user, pass]). Driver передаёт значения отдельно от SQL текста — escape невозможно обойти. Учит: zero string concat с user input в SQL, ORM с bind params, allowlist для dynamic identifiers (ORDER BY нельзя параметризовать).
2. A01 Broken Access Control (IDOR). Юзер логинится, делает GET /api/orders/12345 — свой order. Возвращается успешно. Затем меняет URL на /api/orders/12346 — order другого пользователя. WAF не видит проблемы (выглядит как legit /api/orders/N). Сценарий показывает реальный кейс First American Financial 2019 (885M записей через инкремент document ID) и fix: ownership check на каждом resource access (order.user_id === session.user_id), audit log на suspicious patterns (юзер читает 100 чужих orders/мин). Учит: WAF не помогает с IDOR в принципе — нужен AuthZ middleware с default-deny, ReBAC для sharing.
3. A10 SSRF → AWS IMDS → IAM theft (Capital One 2019). App имеет фичу «import image from URL». Атакующий вместо публичной картинки шлёт url=http://169.254.169.254/latest/meta-data/iam/security-credentials/. Сервер делает outbound HTTP внутри VPC — IMDS отвечает (он доверяет requests из VPC). Возвращается IAM role name → второй запрос на role-specific endpoint → AWS access key, secret, session token. Атакующий теперь имеет legit IAM creds с s3:* → aws s3 sync s3://prod-pii-bucket .. Capital One 2019 — exact эта цепочка (SSRF в WAF + IMDSv1 + over-permissive IAM) → 100M credit card applications.
Fix-stack из 4 слоёв: IMDSv2 (требует session token через PUT — blind SSRF не делает PUT), URL allowlist в app (parse URL, resolve DNS в коде, reject private ranges 169.254.0.0/16, 10/8, 172.16/12, 192.168/16), egress proxy (VPC proxy с destination policy, SG: deny all egress except proxy), least-privilege IAM (s3-reader-role на ОДИН bucket, не s3:*). Учит: одного fix-а не хватает — нужна defense-in-depth. Plus gotcha: DNS rebinding (домен резолвится в публичный IP при validation, в 169.254.169.254 при fetch) — резолви IP один раз в коде, передавай IP в fetch.
ADR-001: Defense-in-depth — где какая защита. Tempting solution — поставить WAF и забыть. Реальность: WAF ловит ~30% (signature-based, known patterns). Остальные 70% — design flaws, business logic, IDOR (выглядит как valid HTTP request). Решение: каждый OWASP пункт mitigated на правильном уровне:
Anti-pattern: WAF as silver bullet. WAF не видит IDOR, не понимает business logic, не помогает с insecure deserialization. Это фильтр known payloads, не замена коду.
ADR-002: A03 SQL Injection — prepared statements vs ORM vs string concat. TalkTalk 2015 (157K records, £77M) и Sony 2011 (1M accounts) — не «редкая ошибка джуна», а #3 в OWASP 20 лет подряд. Решение — жёсткое правило zero string concatenation с user input в SQL. Три приемлемых пути: (1) Parameterized queries всегда первый выбор, driver передаёт параметры отдельно от SQL текста; (2) ORM с bind params (Drizzle/Prisma/SQLAlchemy) — никогда db.execute(SELECT WHERE id=${x}) template literal injection; (3) Allowlist для dynamic identifiers — table/column names нельзя параметризовать, валидируй против явного списка ["created_at","amount"].includes(field).
Ловушки: LIKE '%${q}%' → параметризуй LIKE $1 с value '%' + q + '%'; IN (${ids}) → построй IN ($1,$2,$3) динамически с параметрами; stored procedures НЕ защищают — внутри SP можно сделать dynamic SQL и проиграть. Defense-in-depth: prepared statements (90% защиты) + Zod validation на API edge + DB user с least privilege (read-only пользователь для report queries не сделает DROP TABLE даже при successful injection) + WAF signatures.
ADR-003: A10 SSRF — IMDSv2 не достаточно одной. IMDSv2 (Capital One reaction) сильно поднял bar — блокирует blind SSRF через required session token. Но недостаточно: атакующий с RCE может сделать PUT и GET. И многие cloud services не IMDS-based (GCS metadata, Azure IMDS). Решение: четыре слоя (IMDSv2 + URL allowlist + egress proxy + IAM least-privilege), каждый закрывает свой failure mode. Trade-off — operational complexity (proxy maintenance, SG rules, allowlist updates) vs. blast radius при breach. Для финансовых/PII систем — обязательно все четыре. Для внутренних admin tools — IMDSv2 + IAM least-priv достаточно.
${jndi:ldap://...}, затронул половину Java enterprise за 48 часов. A06./admin-secret-2024/ через wordlists за 30 секунд (gobuster/ffuf). Защищай auth, не путь.git push после accidental commit. Используй secrets manager, pre-commit hooks (git-secrets, gitleaks).PATCH /user req.body → атакующий шлёт {is_admin: true}. Никогда не делай whole-object update с client input.OWASP Top 10 — это минимум, не максимум. Когда его недостаточно:
OWASP Top 10 — это floor, не ceiling. Закрытие всех 10 означает «не уязвим к 95% автоматизированных атак и scriptkiddies». Защита от targeted APT, supply chain attacks от state actors, insider threats — требует кратно больше.