---
course: ПиТПМ
lecture: 11
title: "Лекция №11. Безопасность модулей программного обеспечения. Анализ уязвимостей в зависимостях программного кода (SAST/DAST)."
---

# Лекция №11. Безопасность модулей программного обеспечения. Анализ уязвимостей в зависимостях программного кода (SAST/DAST).

## §11.1. Почему тестировщик должен думать о безопасности

За этот семестр мы научились тестировать многое:

| Лекция | Что тестировали |
| --- | --- |
| **Лекция 6** | Модули и интеграцию с БД |
| **Лекция 7** | Интерфейсы и E2E-сценарии |
| **Лекция 8** | Производительность и нагрузку |
| **Лекция 9** | CI/CD и автоматизацию |
| **Лекция 10** | Совместимость микросервисов |

Осталось последнее: **безопасность**. *Если система работает, но небезопасна — она не работает*. Потому что:

- Данные пользователей могут быть украдены.
- Система может быть взломана.
- Бизнес может потерять репутацию и деньги.

Многие думают, что **безопасность — это задача разработчиков**. Но это не так. Кто отвечает за безопасность в проекте:

| Кто | Что делает |
| --- | --- |
| **Разработчик** | Пишет код и старается избегать уязвимостей |
| **Архитектор** | Проектирует систему с учетом безопасности |
| **DevOps** | Настраивает серверы и сеть |
| **Тестировщик** | Проверяет, что система безопасна |

**Задача тестировщика**

— убедиться, что уязвимости системы выявлены (а затем переданы разработчикам для исправления). Тестировщик анализирует систему с позиции внешнего наблюдателя, действуя подобно потенциальному злоумышленнику.

Тестировщик — последний рубеж перед продакшеном. Вот как выглядит путь кода от разработчика до пользователя:

wireframe

```
Разработчик пишет код → Code Review → CI/CD → Тестировщик → Продакшен

                                                  ↑

                                            ПОСЛЕДНИЙ РУБЕЖ!
```

Что это значит:

| Если тестировщик нашел уязвимость | Если тестировщик пропустил уязвимость |
| --- | --- |
| **Уязвимость исправлена до релиза** | Уязвимость попадает в продакшен |
| **Пользователи в безопасности** | Хакеры могут использовать уязвимость |
| **Репутация компании сохранена** | Скандалы, утечки данных, штрафы |

**OWASP (Open Web Application Security Project)**

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

Зачем это знать тестировщику? Чтобы знать, на что обращать внимание.

Краткий обзор OWASP Top 10 (упрощенно):

| № | Уязвимость | Что это | Пример |
| --- | --- | --- | --- |
| **1** | **Инъекции (SQL, NoSQL, OS)** | Злоумышленник внедряет вредоносный код в запрос | `' OR '1'='1` в поле ввода — и он зашел без пароля |
| **2** | **Потеря аутентификации** | Пароли и токены украдены или слабые | Пароль `123456` — легко взломать |
| **3** | **XSS (межсайтовый скриптинг)** | Злоумышленник внедряет JavaScript на страницу | Пользователь кликает на ссылку — его данные украдены |
| **4** | **Небезопасные зависимости** | Используются устаревшие библиотеки с уязвимостями | Log4j, jQuery с известными CVE |
| **5** | **Неправильная настройка безопасности** | Сервер настроен небезопасно | Открытые порты, слабые пароли по умолчанию |
| **6** | **Уязвимости в API** | API не проверяет права доступа | Можно получить данные другого пользователя |
| **7** | **Недостаточное логирование** | Нет логов для расследования инцидентов | Непонятно, кто и когда взломал систему |

Запомните

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

Что нужно запомнить:

`1.` **Опасность SQL-инъекций** — злоумышленник может прочитать, изменить или удалить данные.

`2.` **Опасность XSS** — злоумышленник может украсть данные пользователей.

`3.` **Опасность устаревших библиотек** — в них уже известны уязвимости, и их легко использовать.

`4.` **Опасность неправильных настроек** — система может быть взломана из-за слабого пароля или открытого порта.

Существует два основных подхода к **тестированию безопасности**. Мы будем подробно разбирать их в следующих параграфах, но пока кратко:

| Подход | Что это | Когда проверяет | Что ищет |
| --- | --- | --- | --- |
| \*\*Static (SAST) \*\* | Проверка кода без запуска | На этапе написания кода | SQL-инъекции, XSS, устаревшие библиотеки |
| **Dynamic (DAST)** | Проверка работающего приложения | На этапе тестирования (staging) | Неправильную авторизацию, открытые порты, уязвимости API |

Простая аналогия:

- **SAST** — это как проверка чертежа здания. Вы смотрите на план и ищете ошибки до того, как начали строить.
- **DAST** — это как проверка уже построенного здания. Вы ходите по коридорам, проверяете замки, смотрите, не открыты ли окна.

## §11.2. SAST: Static Application Security Testing

Давайте разберем что такое **SAST**. **SAST** расшифровывается как **Static Application Security Testing**.

**SAST**

— статическое тестирование безопасности приложений.

Важно знать

Это значит, что **SAST** проверяет код без его запуска. Он анализирует исходный код, ищет потенциальные уязвимости и сообщает о них разработчику.

Пример:

csharp

```csharp
// Вот такой код может содержать уязвимость
public IActionResult GetUser(string userId)
{
    // SAST найдет проблему: SQL-инъекция!
    var query = $"SELECT * FROM Users WHERE Id = '{userId}'";
    //                                                ↑
    //                    userid приходит от пользователя
    //                    злоумышленник может передать: ' OR '1'='1
    var user = db.Execute(query);
    return Ok(user);
}
```

**SAST** ищет не все уязвимости, а только те, которые можно найти в коде. Вот основные категории:

| Что ищет SAST | Пример | Почему это опасно |
| --- | --- | --- |
| **SQL-инъекции** | `"SELECT * FROM Users WHERE Id = '" + userId + "'"` | Злоумышленник может украсть или удалить данные |
| **XSS (межсайтовый скриптинг)** | `Response.Write(userInput)` | Злоумышленник может внедрить JavaScript на страницу |
| **Устаревшие библиотеки** | Использование log4j старой версии с известной уязвимостью | Есть известные CVE, которые легко использовать |
| **Ошибки конфигурации** | Строка подключения с паролем в коде | Пароль виден всем, у кого есть доступ к коду |
| **Небезопасные методы** | Использование MD5 для хеширования паролей | MD5 можно взломать, нужно использовать bcrypt |
| **Отсутствие валидации** | Не проверяется ввод пользователя | Злоумышленник может отправить любые данные |

**SAST** запускается на ранних этапах разработки, как можно раньше.

| Где запускается | Когда | Зачем |
| --- | --- | --- |
| **В IDE (Visual Studio)** | Пока разработчик пишет код | Мгновенная обратная связь |
| **В CI/CD** | При каждом `git push` | Автоматическая проверка |
| **В Code Review** | Перед вливанием в main | Проверка перед мержем |

В IDE это выглядит следующим образом. Разработчик пишет код:

csharp

```csharp
// Разработчик пишет такой код
string query = $"SELECT * FROM Users WHERE Id = '{userId}'";
```

SAST (в IDE) подсвечивает строку красным и пишет:

wireframe

```
⚠️ Уязвимость: Возможна SQL-инъекция. 

Используйте параметризованные запросы вместо конкатенации строк.
```

Разработчик исправляет:

csharp

```csharp
// Исправленный код
var query = "SELECT * FROM Users WHERE Id = @UserId";
// Используем параметры, а не конкатенацию
```

В **§9.7 лекции №9** мы говорили о **линтерах** (*StyleCop, ESLint*). На первый взгляд **SAST** и **линтер** делают похожее — проверяют код без запуска. Но разница есть.

| Характеристика | Линтер | SAST |
| --- | --- | --- |
| **Что ищет** | Стиль и качество кода (отступы, названия) | Уязвимости безопасности (SQL-инъекции, XSS) |
| **Тип ошибок** | Косметические и стилистические | Критические (могут привести к взлому) |
| **Последствия** | Код некрасивый, но работает | Код может быть взломан |
| **Когда использовать** | Всегда | Всегда |

Простая аналогия:

- **Линтер** — это как учитель русского языка, который проверяет, правильно ли вы поставили запятые.
- **SAST** — это как редактор, который проверяет, не написали ли вы в тексте номер банковской карты.

И то, и другое важно, но **SAST** важнее, потому что он находит реальные угрозы.

Для `.NET-проектов` есть несколько популярных инструментов **SAST**:

**1. Security Code Scan**

Это бесплатный анализатор для `.NET`. Он встроен в **Visual Studio** и проверяет код на уязвимости прямо во время написания.

bash

```bash
# Установка через NuGet
dotnet add package SecurityCodeScan
```

Что проверяет:

- SQL-инъекции.
- XSS-уязвимости.
- Использование небезопасных методов.
- Слабые алгоритмы хеширования.

**2. SonarQube**

**SonarQube**

— это мощная платформа для анализа кода. Она проверяет не только уязвимости, но и качество кода, покрытие тестами, дублирование.

yaml

```yaml
# В CI/CD (GitHub Actions)
- name: Run SonarQube analysis
  run: |
    dotnet tool install --global dotnet-sonarscanner
    dotnet sonarscanner begin /k:"project-key" /d:sonar.host.url="http://sonarqube:9000"
    dotnet build
    dotnet sonarscanner end
```

Что проверяет:

- Все, что проверяет Security Code Scan.
- Плюс качество кода, сложность, тестовое покрытие.
- Плюс интеграцию с CI/CD.

**3. OWASP Dependency Check (мы рассмотрим в §11.5)**

Этот инструмент проверяет сторонние библиотеки на наличие **известных уязвимостей**.

Когда SAST находит уязвимость, разработчик видит что-то подобное. Разберем то, что они видят в **IDE (Visual Studio)**:

wireframe

```
⚠️ Security Code Scan: SCS0001

   SQL injection vulnerability detected.

   The query is constructed using string concatenation with user input.

   Recommendation: Use parameterized queries.

   Location: UserController.cs, line 42
```

**В CI/CD (отчет)**:

wireframe

```
=================================================================

Security Code Scan Report

=================================================================

1 critical issues found:

[CRITICAL] SQL injection vulnerability

File: UserController.cs

Line: 42

Description: Query uses string concatenation with user input.

Recommendation: Use parameterized queries.

=================================================================
```

Что делает разработчик:

`1.` Читает сообщение.

`2.` Понимает, где проблема и почему она опасна.

`3.` Исправляет код (использует параметризованный запрос).

`4.` Отправляет исправление снова.

## §11.3. DAST: Dynamic Application Security Testing

**DAST** расшифровывается как **Dynamic Application Security Testing**.

**DAST**

— динамическое тестирование безопасности приложений.

Это значит, что **DAST** проверяет работающее приложение через внешние запросы. Он не смотрит на код, он ведет себя как злоумышленник — отправляет запросы, анализирует ответы и ищет уязвимости.

Чем **DAST** отличается от **SAST**:

| Характеристика | SAST | DAST |
| --- | --- | --- |
| **Что проверяет** | Код (без запуска) | Работающее приложение |
| **Когда** | На этапе написания кода | На этапе тестирования (staging) |
| **Знает код** | Да (анализирует исходники) | Нет (как хакер) |
| **Что находит** | SQL-инъекции в коде, XSS, устаревшие библиотеки | Открытые порты, ошибки авторизации, неправильные заголовки |
| **Требует запущенное приложение** | Нет | Да |
| **Скорость** | Быстро (секунды) | Медленнее (минуты) |

Простая аналогия:

- **SAST** — это как проверка чертежа самолета. Вы смотрите, правильно ли спроектированы крылья.
- **DAST** — это как испытательный полет. Вы запускаете самолет, проверяете, открывается ли шасси, работает ли автопилот.

И **SAST**, и **DAST** нужны. Они находят разные типы проблем.

Что же может найти **DAST**? **DAST** находит уязвимости, которые видны снаружи, без доступа к коду.

| Что ищет DAST | Пример | Почему это опасно |
| --- | --- | --- |
| **Неправильная авторизация** | Можно зайти в админку без логина | Любой может управлять системой |
| **Открытые порты** | Порт 8080 открыт, на нем висит база данных | Хакер может подключиться к БД |
| **Слабые пароли** | Пароль `admin` на тестовом сервере | Легко взломать |
| **Неправильные заголовки** | Нет `X-Frame-Options`, `Content-Security-Policy` | Уязвимость к XSS |
| **Незащищенные эндпоинты** | `GET /api/users` возвращает всех пользователей | Утечка данных |
| **Инъекции** | SQL-инъекции, XSS в работающем приложении | Кража данных |

**DAST** запускается на работающем приложении, когда оно уже развернуто в **тестовой среде** (*staging*).

| Где запускается | Когда | Зачем |
| --- | --- | --- |
| **Staging-среда** | Перед релизом в продакшен | Проверить, что приложение безопасно |
| **Продакшен** | Регулярно (еженедельно, ежемесячно) | Проверить, не появились ли новые уязвимости |
| **Периодически** | По расписанию (ночью) | Не влияет на разработчиков |

Это примерно выглядит следующим образом:

wireframe

```
Разработка → Test → Staging → DAST → Результат → Продакшен

       ↑

DAST проверяет здесь
```

Для `.NET-проектов` (*и не только*) есть несколько популярных инструментов **DAST**. Самый известный и доступный — **OWASP ZAP**.

**1. OWASP ZAP (Zed Attack Proxy)**

Это бесплатный инструмент от **OWASP** для тестирования безопасности веб-приложений. Как работает:

`1.` Вы запускаете OWASP ZAP.

`2.` Указываете URL вашего приложения (например, [https://staging.myapp.com](https://staging.myapp.com/)).

`3.` ZAP начинает сканировать: отправляет запросы, анализирует ответы, ищет уязвимости.

`4.` Вы получаете отчет о найденных проблемах.

Что умеет ZAP:

| Возможность | Что делает |
| --- | --- |
| **Автоматическое сканирование** | Проверяет все эндпоинты на уязвимости |
| **Spider** | Ищет все страницы и эндпоинты приложения |
| **Active Scan** | Активно атакует приложение (SQL-инъекции, XSS) |
| **Passive Scan** | Анализирует ответы без атак |
| **Прокси** | Можно подхватывать и изменять запросы вручную |

Отчет в **OWASP ZAP** выглядит следующим образом:

![](/images/lectures/pitpm/11/image-01.webp)

**2. Коммерческие решения (для больших проектов)**

| Инструмент | Что делает | Когда нужен |
| --- | --- | --- |
| **Burp Suite** | Профессиональный инструмент для тестирования безопасности | Для профессионального пентеста |
| **Qualys** | Облачное сканирование уязвимостей | Для корпоративных проектов |

Как выглядит работа с **OWASP ZAP**? Она выглядит в виде нескольких шагов.

**Шаг №1. Запустить ZAP**

bash

```bash
# Запуск ZAP (из командной строки)
./zap.sh -cmd -quickurl https://staging.myapp.com -quickout report.html
```

**Шаг №2. ZAP сканирует приложение**

**ZAP** отправляет множество запросов к вашему приложению:

wireframe

```
→ GET /api/users

← 200 OK

→ POST /api/login

← 200 OK

→ GET /api/admin

← 403 Forbidden (хорошо, авторизация работает)

→ POST /api/users?sql=...

← 500 Internal Server Error (возможна инъекция!)
```

**Шаг №3. Получить отчет**

wireframe

```
=================================================================

OWASP ZAP Security Report

=================================================================

High risk issues: 2

  1. SQL Injection vulnerability found in /api/users

  2. Cross-Site Scripting found in /api/search

Medium risk issues: 3

  1. Missing security headers (X-Frame-Options)

  2. Sensitive data in response body

  3. Insecure cookie settings

Low risk issues: 1

  1. Directory listing enabled

=================================================================
```

**DAST** можно (и нужно) встраивать в **CI/CD-конвейер**. Как это выглядит в **GitHub Actions**:

yaml

```yaml
name: Security Scan

on:
  push:
    branches: [ main ]

jobs:
  dast-scan:
    runs-on: ubuntu-latest

    steps:
      - name: Deploy application to staging
        run: |
          # Развертываем приложение на тестовую среду
          
      - name: Run OWASP ZAP scan
        uses: zaproxy/action-baseline@v0.11.0
        with:
          target: 'https://staging.myapp.com'
          
      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: dast-report
          path: report.html
```

Что получает команда при интеграции **DAST** в **CI / CD**:

| Результат | Действие |
| --- | --- |
| **✅ 0 уязвимостей** | Релиз разрешен |
| **⚠️ 1 – 2 низкорисковые** | Требуется подтверждение |
| **❌ Высокорисковые** | Релиз блокируется |

## §11.4. SAST vs DAST: что и когда использовать

Мы познакомились с двумя подходами к **тестированию безопасности**:

| Подход | Что проверяет | Когда |
| --- | --- | --- |
| **SAST** | Код (без запуска) | На этапе разработки |
| **DAST** | Работающее приложение | На этапе тестирования |

Важно понять, что это не конкуренты

Они дополняют друг друга. SAST и DAST находят разные типы уязвимостей, и только вместе они дают полную картину безопасности.

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

- **SAST** — это проверка чертежей до начала строительства. Вы смотрите, правильно ли спроектированы стены, крыша, коммуникации.
- **DAST** — это проверка готового дома. Вы ходите по комнатам, проверяете, открываются ли двери, не дует ли из окон, работает ли электричество.

И то, и другое важно. **Если чертежи плохие — дом не будет стоять**. Если чертежи хорошие, но строители сделали ошибки — дом тоже не будет стоять.

Давайте сравним эти два подхода в виде таблицы:

| Характеристика | SAST | DAST |
| --- | --- | --- |
| **Что проверяет** | Код (исходники) | Работающее приложение |
| **Нужен ли запущенный код** | Нет | Да |
| **Знает ли код** | Да (анализирует исходники) | Нет (как хакер) |
| **Когда запускается** | На этапе разработки (IDE, CI/CD) | На этапе тестирования (staging) |
| **Скорость** | Быстро (секунды) | Медленнее (минуты) |
| **Что находит** | SQL-инъекции в коде, XSS, устаревшие библиотеки | Открытые порты, ошибки авторизации, неправильные заголовки |
| **Требует доступа к коду** | Да | Нет |
| **Может найти** | Проблемы в коде | Проблемы в инфраструктуре |
| **Итог** | Проверяет, правильно ли написано | Проверяет, правильно ли работает |

Теперь давайте определим, когда какой инструмент использовать. **SAST** используем:

| Когда | Почему |
| --- | --- |
| **На этапе написания кода (IDE)** | Мгновенная обратная связь |
| **При каждом git push (CI/CD)** | Автоматическая проверка |
| **Перед Code Review** | Найти проблемы до того, как код увидят другие |
| **Для проверки зависимостей** | Найти устаревшие библиотеки |

**DAST** используем:

| Когда | Почему |
| --- | --- |
| **После развертывания в staging** | Проверить работающее приложение |
| **Перед релизом в продакшен** | Финальная проверка |
| **Регулярно в продакшене** | Проверить, не появились ли новые уязвимости |
| **При изменении инфраструктуры** | Проверить, не открылись ли новые порты |

Использование комбинации **SAST + DAST** является полным покрытием безопасности. Использовать только **SAST** — небезопасно, так как:

wireframe

```
SAST нашел проблемы в коде → Разработчик исправил → Релиз

                                                      ↓

                                          ❌ DAST не запускали!

                                          ❌ Не проверены настройки,

                                          ❌ Не проверена авторизация,

                                          ❌ Не проверены заголовки
```

использовать только **DAST** — небезопасно:

wireframe

```
DAST нашел проблемы в работе → Администратор исправил → Релиз

                                                         ↓

                                           ❌ SAST не запускали!

                                           ❌ Не проверены библиотеки,

                                           ❌ Не проверены SQL-инъекции в коде
```

использовать **SAST + DAST** — безопасно:

![](/images/lectures/pitpm/11/image-02.webp)

Что дает комбинация:

| SAST находит | DAST находит | Вместе находят |
| --- | --- | --- |
| **SQL-инъекции в коде** | SQL-инъекции в работе | Всё |
| **XSS в коде** | XSS в работе | Всё |
| **Устаревшие библиотеки** | Неправильные заголовки | Всё |
| **Ошибки конфигурации в коде** | Открытые порты | Всё |

Давайте разберем практический пример. Возьмем, например, сценарий, когда команда разрабатывает интернет – магазин. Без проверки безопасности:

wireframe

```
Разработка → Тесты → Продакшен

                      ↓

          ❌ Уязвимости найдены хакерами

          ❌ Данные пользователей украдены

          ❌ Репутация компании разрушена
```

С **SAST** (*половина решения*):

wireframe

```
Разработка → SAST → Тесты → Продакшен

                      ↓

          ✅ Уязвимости в коде найдены

          ❌ Но уязвимости в настройках остались
```

С **SAST + DAST** (*полное решение*):

wireframe

```
Разработка → SAST → Тесты → DAST (Staging) → Продакшен

                          ↓                      ↓

                  ✅ Уязвимости в      ✅ Релиз безопасен

                  коде и настройках

                  найдены и исправлены
```

Давайте приведем простую аналогию, например, проверка чертежа и проверка готового здания. Проверка чертежа (**SAST**). Вы — архитектор. Вы смотрите на чертеж дома. Вы проверяете:

- Правильно ли расположены стены?
- Правильно ли рассчитана нагрузка?
- Есть ли запас прочности?

Проверка готового здания (DAST). Вы — инспектор по строительству. Вы ходите по построенному дому. Вы проверяете:

- Стоят ли стены ровно?
- Не протекает ли крыша?
- Закрываются ли двери?
- Есть ли запасной выход?

## §11.5. Анализ уязвимостей в зависимостях (SCA)

**SCA** расшифровывается как **Software Composition Analysis**.

**Software Composition Analysis**

— анализ состава программного обеспечения.

Это значит, что **SCA** проверяет все сторонние библиотеки (*NuGet-пакеты, npm-пакеты*), которые использует ваш проект, и ищет среди них известные уязвимости.

Вот, например, вы покупаете готовый торт в магазине. Вы не знаете, из каких ингредиентов он сделан. **SCA** — это как проверка всех ингредиентов: «Этот шоколад просрочен? Эта мука безопасна?».

Современные проекты редко пишут всё с нуля. Мы используем готовые библиотеки. Обычно, 90% кода – чужие библиотеки.

| Тип библиотеки | Пример | Зачем |
| --- | --- | --- |
| **Фреймворки** | ASP.NET Core, React | Основа приложения |
| **Утилиты** | Newtonsoft.Json, Lodash | Сериализация, обработка данных |
| **Работа с БД** | Entity Framework, Sequelize | Работа с базой данных |
| **Тестирование** | xUnit, Jest, Playwright | Написание тестов |

Сколько кода в проекте — ваш, а сколько — чужие библиотеки?

![](/images/lectures/pitpm/11/image-03.webp)

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

Например, вы строите дом. Вы сами построили стены и крышу (ваш код). Но вы купили готовые окна (библиотека). Если окна плохие — дом будет холодным, даже если стены идеальные.

Чем же опасны устаревшие библиотеки? CVE (Common Vulnerabilities and Exposures) — это база данных известных уязвимостей. Каждая уязвимость получает свой номер (например, *CVE-2021-44228*). Пример: уязвимость в **Log4j** (*CVE-2021-44228*).

В 2021 году в популярной Java-библиотеке **Log4j** нашли **критическую уязвимость**. Это позволило хакерам выполнять произвольный код на серверах, где использовалась эта библиотека.

| Что произошло | Последствия |
| --- | --- |
| **Log4j использовали тысячи приложений** | Огромное количество систем было уязвимо |
| **Хакеры нашли уязвимость** | Начались массовые атаки |
| **Нужно было срочно обновлять библиотеку** | Команды работали сутками, чтобы исправить |

А чем же опасны устаревшие библиотеки:

| Опасность | Что это значит |
| --- | --- |
| **Известные уязвимости** | Если CVE известно, хакеры могут использовать его |
| **Легкая эксплуатация** | Часто есть готовые скрипты для атаки |
| **Сложно обнаружить** | Уязвимость не в вашем коде, а в библиотеке |
| **Быстрое распространение** | Одна уязвимость может затронуть тысячи проектов |

Для `.NET-проектов` есть несколько инструментов для анализа зависимостей.

**1. dotnet list package --vulnerable (встроенный)**

Это встроенная команда в `.NET SDK`, которая проверяет пакеты на известные уязвимости.

bash

```bash
# Проверить уязвимости в проекте
dotnet list package --vulnerable

# Вывод
The following packages were found with known vulnerabilities:
┌────────────────────────────────────────────────────────────┐
│ Package                   │ Severity  │ Advisory URL       │
├───────────────────────────┼───────────┼────────────────────┤
│ Newtonsoft.Json 12.0.1    │ High      │ https://...        │
│ Microsoft.Data.SqlClient  │ Moderate  │ https://...        │
└────────────────────────────────────────────────────────────┘
```

Она показывает **название пакета**, **текущую версию**, **уровень серьезности** и **ссылку на описание уязвимости**.

**2. OWASP Dependency Check**

Бесплатный инструмент от **OWASP** для проверки зависимостей.

bash

```bash
# Запуск проверки
dependency-check --scan ./src --format HTML --out ./reports
```

Она проверяет все зависимости (*NuGet, npm, Maven*) на наличие известных **CVE**.

**3. Snyk**

Популярный инструмент для проверки безопасности зависимостей.

bash

```bash
# Установка Snyk
npm install -g snyk

# Проверка .NET-проекта
snyk test --dotnet
```

Что дает **Snyk**:

- Проверка на уязвимости.
- Рекомендации по исправлению (какую версию установить).
- Интеграция с CI/CD.

**SCA** нужно встраивать в **CI/CD** так же, как и другие проверки безопасности.

Как это выглядит в **GitHub Actions**:

yaml

```yaml
name: SCA Scan
on:
  push:
    branches: [ main, develop ]
jobs:
  dependency-check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4      
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0'      
      - name: Run SCA scan (dotnet)
        run: |
          dotnet list package --vulnerable      
      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: sca-report
          path: sca-report.txt
```

Как это выглядит в **GitLab CI**:

yaml

```yaml
dependency-scan:
  stage: test
  image: mcr.microsoft.com/dotnet/sdk:8.0
  script:
    - dotnet list package --vulnerable
  artifacts:
    paths:
      - sca-report.txt
```

Что делать, если найдена уязвимость? Следовать следующим шагам:

| Шаг | Что делать | Почему |
| --- | --- | --- |
| **1. Посмотреть CVE** | Открыть ссылку с описанием уязвимости | Понять, насколько это опасно |
| **2. Проверить версию** | Узнать, какая версия библиотеки используется | Понять, подвержена ли она |
| **3. Обновить библиотеку** | Перейти на версию, в которой исправлена уязвимость | Устранить проблему |
| **4. Протестировать** | Запустить тесты, чтобы убедиться, что ничего не сломалось | Убедиться, что обновление безопасно |
| **5. Отправить код** | Запушить изменения, CI/CD проверит снова | Получить зеленый отчет |

Если обновление невозможно:

| Вариант | Что делать |
| --- | --- |
| **Поискать альтернативу** | Использовать другую библиотеку |
| **Написать обертку** | Изолировать использование библиотеки |
| **Принять риск** | Но задокументировать его |

## §11.6. Интеграция безопасности в CI/CD (DevSecOps)

Мы уже изучили **три инструмента безопасности**. Теперь посмотрим, как они встраиваются в **конвейер**. Схема конвейера с безопасностью:

![](/images/lectures/pitpm/11/image-04.webp)

Этапы **конвейера** с **проверкой безопасности** проходят в **несколько этапов**.

**Этап №1. SAST при каждом push (быстро)**

Запускается сразу после того, как разработчик отправляет код.

yaml

```yaml
# GitHub Actions (упрощенно)
- name: Run SAST scan
  run: dotnet run --project SecurityCodeScan
```

Он проверяет **SQL-инъекции**, **XSS**, **устаревшие методы**, **ошибки конфигурации**. Время выполнения примерно `10 – 30 секунд`.

В случае возникновения ошибки, **конвейер падает**. Разработчик исправляет **уязвимость в коде**.

**Этап №2. SCA при каждом push (быстро)**

Запускается параллельно с **SAST** или сразу после него.

yaml

```yaml
- name: Run SCA scan
  run: dotnet list package --vulnerable
```

Он проверяет **устаревшие библиотеки** с **известными уязвимостями** (*CVE*). Время выполнения примерно `10 – 30 секунд`.

В случае возникновения ошибки, **конвейер падает**. Разработчик обновляет **уязвимую библиотеку**.

**Этап №3. DAST на развернутом приложении (в staging)**

Запускается только после того, как приложение развернуто в **staging-среде**.

yaml

```yaml
- name: Run DAST scan
  run: |
    # Развернуть приложение в staging
    # Запустить OWASP ZAP против staging-приложения
```

Он проверяет **открытые порты**, **ошибки авторизации**, **неправильные заголовки**, **инъекции в работающем приложении**. Время выполнения примерно `2 – 5 минут` (*медленнее, чем SAST*).

В случае возникновения ошибки, **конвейер падает**. Команда исправляет **настройки или код**.

Что делать, если уязвимость найдена: **падать** или **только предупреждать**? Это главный вопрос при настройке безопасности в **CI/CD**.

| Подход | Что происходит | Когда применять |
| --- | --- | --- |
| **Конвейер падает** | Релиз блокируется | Для критических уязвимостей (SQL-инъекции, утечка данных) |
| **Только предупреждение** | Релиз продолжается | Для низкорисковых проблем (стиль, рекомендации) |

В случае, если **конвейер падает**:

yaml

```yaml
- name: Run SAST scan
  run: dotnet run --project SecurityCodeScan
  # Если SAST нашел критическую уязвимость — конвейер падает
```

Когда использовать:

- **SQL-инъекции**.
- **XSS**.
- **Критические CVE в библиотеках**.
- **Открытые порты в staging**.

Почему:

- Эти уязвимости легко использовать для атаки.
- Они могут привести к утечке данных.
- Их нужно исправлять до релиза.

Только **предупреждение**:

yaml

```yaml
- name: Run SAST scan
  run: dotnet run --project SecurityCodeScan
  continue-on-error: true
  # Даже если SAST нашел проблемы — конвейер продолжается
```

Когда использовать:

- Низкорисковые уязвимости (некритические).
- Рекомендации по улучшению (не ошибки).
- Устаревшая, но безопасная библиотека.

Почему:

- Проблема не критична для релиза.
- Ее можно исправить в следующих версиях.

Это сложно понять на первый взгляд, но если знать правило **«80 / 20»**, то все становится просто.

Рассмотрим это правило:

| Уровень серьезности | Действие | Пример |
| --- | --- | --- |
| **Критическая (Critical)** | Конвейер падает ❌ | SQL-инъекция, утечка паролей |
| **Высокая (High)** | Конвейер падает ❌ | XSS, известная уязвимость в библиотеке |
| **Средняя (Medium)** | Предупреждение ⚠️ | Рекомендация по улучшению |
| **Низкая (Low)** | Предупреждение ⚠️ | Стилистическая проблема |

**Конвейер** для разработчиков выглядит в виде **нескольких сценариев**.

**Сценарий №1. Всё хорошо**

wireframe

```
✅ SAST (10s)    → ✅ Все хорошо

✅ SCA (10s)     → ✅ Все хорошо

✅ Build (30s)   → ✅ Собрано

✅ Tests (60s)   → ✅ Все тесты прошли

✅ Deploy (60s)  → ✅ Развернуто

✅ DAST (120s)   → ✅ Все хорошо

✅ Конвейер зеленый → Релиз разрешен!
```

**Сценарий №2. SAST нашел уязвимость**

wireframe

```
❌ SAST (10s)    → ❌ Найдена SQL-инъекция!

   → Конвейер остановлен!

   → Разработчик: исправляю уязвимость.

   → Отправляю исправление.

   → Конвейер снова зеленый.
```

**Сценарий №3. DAST нашел уязвимость**

wireframe

```
✅ SAST (10s)    → ✅ Все хорошо

✅ SCA (10s)     → ✅ Все хорошо

✅ Build (30s)   → ✅ Собрано

✅ Tests (60s)   → ✅ Все тесты прошли

✅ Deploy (60s)  → ✅ Развернуто

❌ DAST (120s)   → ❌ Найдены открытые порты!

   → Конвейер остановлен!

   → DevOps: закрываю порты.

   → Отправляю исправление.

   → Конвейер снова зеленый.
```

## §11.7. Антипаттерны в тестировании безопасности

В этой лекции мы изучили инструменты безопасности: **SAST, DAST, SCA**. Но инструменты — это только половина дела. Вторая половина — правильное использование.

Есть несколько типичных ошибок, которые сводят на нет все усилия по обеспечению безопасности. Они превращают безопасность из реальной защиты в иллюзию.

Главная мысль этого параграфа заключается в том, что **безопасность —это постоянный процесс**. И каждый член команды за него отвечает.

**Антипаттерн №1. «Безопасность — это не моя работа»**

Почему это опасно:

| Кто так думает | К чему это приводит |
| --- | --- |
| **Разработчик** | Пишет код с уязвимостями (SQL-инъекции, XSS) |
| **Тестировщик** | Не проверяет безопасность, пропускает уязвимости |
| **DevOps** | Настраивает сервер с открытыми портами |
| **Все вместе** | Система уязвима, данные пользователей под угрозой |

Как должно быть правильно:

| Кто | Что делает |
| --- | --- |
| **Разработчик** | Пишет безопасный код (использует параметризованные запросы, валидацию) |
| **Тестировщик** | Проверяет, что код безопасен (запускает SAST, проверяет авторизацию) |
| **DevOps** | Настраивает сервер безопасно (закрытые порты, HTTPS) |
| **Все вместе** | Безопасность — общая ответственность |

Для примера давайте посмотрим на аналогию. Безопасность в офисе — это не задача только охраны. Это задача всех. Каждый сотрудник закрывает дверь, не оставляет документы на столе, не сообщает пароли посторонним. Если кто-то думает «это не моя работа» — офис становится уязвимым.

**Антипаттерн №2. «Мы проверили SAST, DAST не нужен»**

Почему это опасно? SAST проверяет код, но не проверяет:

| Что проверяет SAST | Что НЕ проверяет SAST |
| --- | --- |
| **SQL-инъекции в коде** | SQL-инъекции в работающем приложении |
| **XSS в коде** | XSS в работающем приложении |
| **Устаревшие библиотеки** | Открытые порты |
| **Ошибки конфигурации в коде** | Неправильные заголовки в ответах |

Пример:

wireframe

```
SAST: ✅ Все хорошо. Код безопасен.

DAST: ❌ Найдена проблема: порт 8080 открыт, база данных доступна извне.

      ❌ Найдена проблема: отсутствуют заголовки безопасности.
```

Код безопасен, но система уязвима. Без **DAST** вы этого не узнаете.

Вот, например, вы проверили двигатель машины (**SAST**). Он работает идеально. Но вы не проверили, открыты ли двери, работает ли сигнализация, есть ли запасное колесо (**DAST**). Машина поедет, но она небезопасна.

**Антипаттерн №3. «Запустили один раз и забыли»**

Почему это опасно:

| Что происходит со временем | Последствия |
| --- | --- |
| **Появляются новые CVE в библиотеках** | Вы используете устаревшие, уязвимые версии |
| **Изменяются стандарты безопасности** | Вы не соответствуете современным требованиям |
| **Появляются новые типы атак** | Вы не защищены от новых угроз |
| **Разработчики меняют код** | Новый код может содержать уязвимости |

Пример:

wireframe

```
Месяц 1: Настроили SAST ✅

Месяц 3: Появилась уязвимость в Newtonsoft.Json (CVE)

         SAST не обновлен → уязвимость не найдена

Месяц 6: Хакеры используют эту уязвимость ❌
```

Как должно быть правильно:

| Что делать | Как часто |
| --- | --- |
| **Обновлять правила SAST** | Ежемесячно |
| **Проверять новые CVE** | Еженедельно |
| **Пересматривать настройки безопасности** | После каждого релиза |
| **Обновлять инструменты** | При выходе новых версий |

**Антипаттерн №4. «Игнорируем предупреждения»**

Почему это опасно:

| Уровень предупреждения | Что это значит | Если игнорировать |
| --- | --- | --- |
| **Информация (Info)** | Рекомендация | Нет проблем |
| **Низкая (Low)** | Потенциальная проблема | Может стать уязвимостью |
| **Средняя (Medium)** | Реальная проблема | Может привести к атаке |
| **Высокая (High)** | Критическая уязвимость | Атака почти гарантирована |

Пример:

wireframe

```
SAST: ⚠️ Предупреждение: используется MD5 для хеширования паролей.

       Рекомендация: использовать bcrypt.

Разработчик: "Ну, работает же. Не буду трогать."

Через месяц:

Хакеры взламывают пароли (MD5 уязвим) ❌
```

**Желтые предупреждения** становятся **красными уязвимостями**.

Сравнительная таблица антипаттернов представлена ниже:

| Антипаттерн | Симптом | Последствия | Решение |
| --- | --- | --- | --- |
| **«Не моя работа»** | Никто не отвечает за безопасность | Система уязвима, все ждут другого | Безопасность — задача всех |
| **«SAST достаточно»** | Запускают только SAST | DAST находит проблемы в работе | Использовать SAST + DAST |
| **«Запустили и забыли»** | Инструменты не обновляются | Новые уязвимости не находятся | Обновлять регулярно |
| **«Игнорируем предупреждения»** | Желтые отчеты закрываются | Предупреждения становятся уязвимостями | Исправлять все, даже низкорисковые |

В заключение нужно разобрать что делать и как это делать правильно при работе с тестированием:

| Что нужно делать | Как |
| --- | --- |
| **Признать ответственность** | Безопасность — задача каждого члена команды |
| **Использовать все инструменты** | SAST + DAST + SCA — не один, а все |
| **Обновлять регулярно** | Инструменты, правила, библиотеки |
| **Не игнорировать предупреждения** | Исправлять все проблемы, даже низкорисковые |
| **Проверять снова** | После исправления — перезапускать проверку |
