﻿# OWASP Top Ten 2021 через простые примеры на Java\. И немного про SAST

В этой статье мы расскажем про категории OWASP Top Ten 2021 через призму срабатываний Java анализатора PVS\-Studio\. Так что, если у вас есть желание посмотреть на возможные паттерны уязвимостей в Java коде или узнать, что из себя представляют категории OWASP Top Ten, приятного чтения\!

![1287_java_sast_ru/image1.png](https://import.viva64.com/docx/blog/1287_java_sast_ru/image1.png)

## Введение

Мы — компания PVS\-Studio, разрабатывающая одноимённый статический анализатор уже практически 18 лет\. Работаем с языками C/C\+\+, C\# и Java\. С недавнего времени активно занялись тем, чтобы Java анализатор мог называться SAST\-решением\. Ведь Java — один из [популярнейших](https://www.tiobe.com/tiobe-index/#top20) языков, на котором разрабатывают web\-приложения\.

<details>
   <summary>SAST?</summary>

[SAST](https://pvs-studio.ru/ru/blog/terms/6631/) \(Static Application Security Testing\) — это методология тестирования разрабатываемого приложения, которая основана на поиске потенциальных уязвимостей посредством анализа его исходного кода\. Т\.е\. [статический анализ](https://pvs-studio.ru/ru/blog/terms/0046/), ориентированный на поиск ошибок безопасности\. Это помогает предотвращать определённые проблемы безопасности ПО ещё до его выхода в релиз\. Подробнее можете ознакомиться [здесь](https://pvs-studio.ru/ru/blog/terms/6631/)\.


</details>
И нас часто спрашивают: "Как вы выбираете диагностики, которые добавляете в анализатор? Чем руководствуетесь?" В контексте SAST\-направления в Java для нас одним из главных ориентиров является OWASP Top Ten\.

[OWASP Top Ten](https://owasp.org/www-project-top-ten/) — это проект организации OWASP, представляющий собой рейтинг сгруппированных web\-уязвимостей\. Формируется он раз в несколько лет на основе отчёта специалистов и консультантов по информационной безопасности, bug bounty компаний и самих разработчиков web\-продуктов\. В этой статье мы рассмотрим список, сформированный к 2021 году\. Однако здесь хочу отметить, что в ближайшее время [должен выйти](https://owasp.org/www-project-top-ten/#owasp-top-ten-2025) OWASP Top Ten 2025\. В нашем блоге обязательно будет обзор, так что подписывайтесь и следите\.

**А насколько SAST и OWASP Top Ten важны и актуальны?** С каждым годом рост обнаруженных уязвимостей только растёт\. Это можно наблюдать по количеству реальных зарегистрированных уязвимостей согласно статистике от [CVE](https://www.cve.org/)\.

<details>
   <summary>CVE?</summary>

CVE \(Common Vulnerabilities and Exposures\) — база данных общеизвестных уязвимостей информационной безопасности\. Подробнее ознакомиться можно [здесь](https://pvs-studio.ru/ru/blog/terms/6628/)\.


</details>
Здесь я приведу краткую, но показательную выжимку их статистики по найденным уязвимостям с 2014 по 2024 годы:

|2014|2015|2016|2017|2018|2019|2020|2021|2022|2023|2024|
|---|---|---|---|---|---|---|---|---|---|---|
|7948|6494|6457|14645|16512|17308|18375|20161|25059|28961|40077|

С полной статистикой также можете ознакомиться по [ссылке](https://www.cve.org/About/Metrics#PublishedCVERecords)\.

Вы можете видеть, что количество обнаруживаемых уязвимостей только растёт\. И в их превентивном обнаружении может помочь максимально полное и всестороннее тестирование\. Ведь у каждого метода тестирования своя зона ответственности\. Если использовать не все, определённая часть проблем уйдёт в тень\. 

Именно всестороннее тестирование помогает придерживаться [shift\-left](https://en.wikipedia.org/wiki/Shift-left_testing) принципа\. Если проблема была найдена во время статического или динамического анализа, а не во время ручного тестирования или уже в продакшне, затраты на её исправление будут гораздо меньше\. В связи с этим потребность в SAST\-инструментах может только расти\.

Практически весь набор необходимых механизмов у нас был\. Чтобы иметь возможность покрыть необходимые ситуации, Java команда активно работает над его улучшением\.


> \*\*Примечание\\\.\*\* Ранее мы уже рассказывали о том, что делали для улучшения нашего SAST инструмента\\\. Среди этого, например, технология анализа помеченных данных \\\(\[статья N1\]\(https://pvs\-studio\.ru/ru/blog/posts/java/1198/\), \[статья N2\]\(https://pvs\-studio\.ru/ru/blog/posts/java/1228/\)\\\), \[пользовательское аннотирование\]\(https://pvs\-studio\.ru/ru/blog/posts/java/1274/\) кода, а также различные улучшения технологии анализа потока данных\\\.

И по итогу наш анализатор мог покрыть 9/10 категорий OWASP Top Ten\.

## Категории OWASP Top Ten 2021

Опираться мы будем на позиции OWASP Top Ten по порядку\. Мы расскажем вам про саму категорию и после покажем один или несколько примеров уязвимого кода, на который PVS\-Studio выдаёт сообщение, чтобы было понятнее, как это может выглядеть на практике\.

### A01:2021 — Broken Access Control

[**Broken Access Control**](https://owasp.org/Top10/A01_2021-Broken_Access_Control/) — категория, которая объединяет в себе уязвимости, приводящие к несанкционированному раскрытию информации, изменению или уничтожению всех данных или выполнению бизнес\-функций за пределами полномочий пользователя\.

Первый пример:

```cpp
public class RedirectServlet extends HttpServlet {
  
  @Override
  protected void doGet(HttpServletRequest request, 
                       HttpServletResponse response) throws IOException {
    String query = request.getQueryString();
    if (query.contains("url")) {
      String url = request.getParameter("url");
      response.sendRedirect(url);               // <=
    }
  }
}
```

Срабатывание PVS\-Studio: [V5324](https://pvs-studio.ru/ru/docs/warnings/v5324/) Possible open redirect vulnerability\. Potentially tainted data in the 'url' variable is used in the URL\. RedirectServlet\.java 15, 14

Сервер принимает `GET` запрос, берёт из него переданный URL\-адрес и без проверки перенаправляет по нему пользователя\. Проблема в том, что злоумышленнику ничего не мешает создать фишинговую копию основного сайта и сделать скомпрометированную ссылку, например:

```cpp
https://notevil.com/redirect?url=https://evil.com/registration
```

В этом случае наш сайт перенаправит пользователя на форму регистрации на сайте злоумышленника\. И пользователь, думая, что находится на доверенном сайте, может ввести свои конфиденциальные данные\. А они в свою очередь направятся прямиком злоумышленнику\.

Чтобы этого не допустить, серверу перед перенаправлением стоит проверять ссылку\. И если она не в числе доверенных — перенаправление должно быть запрещено:

```cpp
private static final List<String> ALLOWED_DOMAINS = List.of(
  "https://allowed-host1.com",
  "https://allowed-host2.com",
  "https://allowed-host3.com"
);

public class RedirectServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest request, 
                       HttpServletResponse response) throws IOException {
    String query = request.getQueryString();
    if (query.contains("url")) {
      String url = request.getParameter("url");
      if (ALLOWED_DOMAINS.contains(url)) {      // <=
        response.sendRedirect(url);              
      } else {
        //....
      }
    }
  }
}
```

Это лишь один вариант защиты\. C более обширным списком вариантов защиты от этой проблемы, вы можете ознакомиться в специальной рубрике [Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) в блоге OWASP\. В ней собраны рекомендации по предотвращению различных уязвимостей и проблем безопасности\.

Рассмотрим пример другой ситуации:

```cpp
private final Path uploadDir = Path.of("/var/www/uploads");

@Override
public void init() throws ServletException {
  try {
    Files.createDirectories(uploadDir);
    Files.setPosixFilePermissions(
            uploadDir,
            PosixFilePermissions.fromString("rwxrwxrwx"));  // <=
  } catch (IOException e) {
      throw new ServletException(e);
  }
}

@Override
protected void doPost(HttpServletRequest req,
                      HttpServletResponse resp) throws .... {
  Part filePart = req.getPart("file");
  Path target = uploadDir.resolve(Paths.get(filePart.getSubmittedFileName()));
  try (InputStream in = filePart.getInputStream()) {
    Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
  }
  resp.getWriter().println("uploaded to " + target);
}
```

Срабатывание PVS\-Studio: [V5318](https://pvs-studio.ru/ru/docs/warnings/v5318/) Setting loose POSIX file permissions \(rwxrwxrwx\) is security\-sensitive\. PermissionsExample\.java 13

Здесь каталогу, в который загружаются файлы, выставляются все возможные права группе OTHERS\. В том числе права на выполнение файла\. И если пользователь загрузит исполняемый файл \(к примеру, `shell.jsp`\), то при обращении к нему он выполнится \(в случае, если мы работаем с Tomcat сервером\):

```cpp
https://example.com/uploads/shell.jsp
```

Так, небезопасная конфигурация прав доступа для специальной директории веб\-приложения привела к RCE \(удалённое выполнение кода\)\. Чтобы подобных вещей не происходило, важно придерживаться принципа минимальных привилегий\. А статический анализ вам поможет не проглядеть такие моменты\. 

### A02:2021 — Cryptographic Failures

Категория [**Cryptographic Failures**](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/) объединяет группу уязвимостей, связанных с некорректным шифрованием конфиденциальных данных: использование устаревших криптографических алгоритмов или хеш\-функций, передача информации в незашифрованном виде и т\.д\.

Пример:

```cpp
public static void callExternalApi() throws Exception {
  SSLContext sslContext = SSLContext.getInstance("TLSv1");     // <=
  sslContext.init(null, null, new java.security.SecureRandom());

  HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
  URL url = new URL("https://external-api.example.com/data");
  
  HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
  try (BufferedReader in = new BufferedReader(
          new InputStreamReader(conn.getInputStream()))
  ) {
    String inputLine;
    while ((inputLine = in.readLine()) != null)
      System.out.println(inputLine);
  }
}
```

Срабатывание PVS\-Studio: [V5313](https://pvs-studio.ru/ru/docs/warnings/v5313/) Do not use old versions of SSL/TLS protocols as it may cause security issues\. Insecure protocols: TLSv1\. SSLExample\.java 11

В этом примере для создания соединения используется устаревшая версия протокола SSL/TLS\. Такая небезопасная конфигурация способна привести к таким атакам, как [Man\-In\-The\-Middle](https://en.wikipedia.org/wiki/Man-in-the-middle_attack), её подвиду — [BEAST](https://en.wikipedia.org/wiki/Transport_Layer_Security#BEAST_attack), и т\.д\. Если конкретнее, то злоумышленником возможен перехват и расшифровка/изменение данных, которые передаются между двумя сторонами\. 

Для того, чтобы обезопасить себя, лучше воспользоваться более новой и безопасной версией этого алгоритма — "TLSv1\.2" или "TLSv1\.3"\.

Подобные проблемы могут быть наследием legacy\-части проекта, и, опять же, автоматизированный поиск подобных проблем посредствам SAST\-инструментов — лучший способ не допустить подобного\. 

### A03:2021 — Injection

[**Injection**](https://owasp.org/Top10/A03_2021-Injection/) — уязвимости, которые возникают за счёт того, что ненадёжные данные при попадании в определённые критические места \(к примеру, исполнение SQL\-запроса\) заставляют программу вести себя не так, как планировалось изначально\. Как следствие, возможна порча конфиденциальных данных, их обнародование или остановка выполнения программы\.

Подобных диагностик у нас предостаточно\. Покажу на примере самой типовой — SQL\-инъекции\. Предыдущие примеры занимали собой не более одного метода\. Давайте рассмотрим создание SQL\-запроса на более приближенном к реальности примере — простом MVC\-контроллере\. Заодно и проверим, сможет ли PVS\-Studio найти проблему в примере побольше и посложнее\.

DemoController\.java:

```cpp
@RestController
public class DemoController {
  @Autowired
  private DemoService service;

  @RequestMapping("demo")
  public ResponseEntity<DemoObject> 
            demoEndpoint(@RequestParam(name="name") String name
  ) {
    return service.findByName(name)
                  .map(ResponseEntity::ok)
                  .orElse(ResponseEntity.notFound().build());
  }
}
```

В контроллер приходит запрос, из которого извлекается параметр `name` и передаётся в `DemoService`\.

DemoService\.java:

```cpp
@Service
public class DemoService {
  @Autowired
  DemoRepository demoRepository;

  Optional<DemoObject> findByName(String name) {
    return demoRepository.findByName(name);
  }
}
```

`DemoService` обращается к репозиторию `DemoRepository`, передавая ему пришедший `name` в качестве параметра\.

DemoRepository\.java:

```cpp
@Repository
public class DemoRepository {
  @Autowired
  private JdbcTemplate jdbcTemplate;

  Optional<DemoObject> findByName(String name) {
    var sql = "SELECT * FROM demoTable WHERE name = '" + name + "'";
    if (name.equals("demoCondition")) {
      sql = "SELECT * FROM demoTable WHERE name = demoName";
      return Optional.ofNullable(
              jdbcTemplate.queryForObject(sql, DemoObject.class));
    }
    return Optional.ofNullable(
            jdbcTemplate.queryForObject(sql, DemoObject.class));
  }
}
```

В репозитории мы формируем SQL\-запрос для базы данных\.

На этот код анализатор PVS\-Studio выдаёт следующее срабатывание: [V5309](https://pvs-studio.ru/ru/docs/warnings/v5309/) Possible SQL injection\. Potentially tainted data in the 'sql' variable is used to create SQL command\. DemoRepository\.java 23, DemoController\.java 16

Само формирование запроса и является главной загвоздкой всей этой конструкции\. Покажу нагляднее:

```cpp
var sql = "SELECT * FROM demoTable WHERE name = '" + name + "'";
```

Запрос формируется через конкатенацию\. И если значение у `name` будет следующим: 

```cpp
' or 1==1; drop table demoTable; --
```

То таблица `demoTable` будет удалена\.

По пути от контроллера до самого запроса данные никак не очистили и не санитизировали\. Анализатор это обнаружил и выдал сообщение\. Чтобы обезопасить своё приложение, данные перед передачей в запрос необходимо либо очищать, либо экранировать\. 

Рекомендуемый способ защиты в контексте SQL\-инъекции — параметризированные запросы:

```cpp
Optional<DemoObject> findByName(String name) {
  String sql;
  if ("demoCondition".equals(name)) {
    sql = "SELECT * FROM demoTable WHERE name = demoName";
    return Optional.ofNullable(
      jdbcTemplate.queryForObject(
        sql,
        new BeanPropertyRowMapper<>(DemoObject.class)
      )
    );
  }

  sql = "SELECT * FROM demoTable WHERE name = ?";
  return Optional.ofNullable(
    jdbcTemplate.queryForObject(
      sql,
      new BeanPropertyRowMapper<>(DemoObject.class),
      name
    )
  );
}
```

### A04:2021 — Insecure Design

[**Insecure Design**](https://owasp.org/Top10/A04_2021-Insecure_Design/) — достаточно широкая категория, объединяющая в себе группу уязвимостей, которые возникают вследствие недостаточно безопасного архитектурного дизайна приложения\.

Одну из подобных ситуаций мы уже рассмотрели, когда я рассказывал вам про категорию A01\. К этой же категории относятся выставление слишком широких прав доступа группе OTHERS и диагностика [V5318](https://pvs-studio.ru/ru/docs/warnings/v5318/), которая подобные ситуации находит\. Но, поскольку этот вариант уязвимости мы уже рассмотрели, давайте взглянем на ещё один:

```cpp
@Override
protected void doGet(HttpServletRequest req,
                     HttpServletResponse res) throws IOException {
  String requestedSessionId = req.getRequestedSessionId();
  if (requestedSessionId != null && requestedSessionId.startsWith("ADMIN-")) {
    // ....
  }
  //....
}
```

Срабатывание PVS\-Studio: [V5316](https://pvs-studio.ru/ru/docs/warnings/v5316/) The use of HttpServletRequest\#getRequestedSessionId is discouraged as it may expose security risks\. DemoServlet\.java 15

Эта ситуация является одним из возможных паттернов реализации [CWE\-807](https://cwe.mitre.org/data/definitions/807.html): здесь при принятии решения в security\-контексте полагаются на недостоверные данные\. 

<details>
   <summary>CWE?</summary>

[CWE](https://pvs-studio.ru/ru/blog/terms/6476/) \(Common Weakness Enumeration\) — поддерживаемая и развиваемая сообществом система классификации недостатков безопасности\. 



Классификация CWE так же, как и OWASP Top Ten, является для нас ориентиром при добавлении новых диагностик\. И у нас [на сайте](https://pvs-studio.ru/ru/pvs-studio/sast/cwe/) вы можете ознакомиться с тем, как наши диагностики соотносятся со CWE идентификаторами\.


</details>
Метод `getRequestSessionId` возвращает тот ID сессии, который задал пользователь, а не тот, который на самом деле сессии принадлежит\. 

С точки зрения архитектуры полагаться на ввод пользователя при принятии решения небезопасно\. Рекомендуется использовать актуальный ID сессии\. В этом случае исправление будет достаточно простым:

```cpp
request.getSession().getId();
```

### A05:2021 — Security Misconfiguration

[**Security Misconfiguration**](https://owasp.org/Top10/A05_2021-Security_Misconfiguration/) — категория, которая объединяет в себе уязвимости, связанные с небезопасной конфигурацией: включение лишних портов или служб, небезопасная настройка, использование данных извне для определения системных свойств и т\.д\.

Пример:

```cpp
public class MisconfigurationExample extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest req,
                        HttpServletResponse resp) throws IOException {
    String key = req.getParameter("key");
    String value = req.getParameter("value");

    if (key == null || value == null || key.isBlank()) {
      resp.sendError(400, "Invalid key or value");
      return;
    }

    System.setProperty(key, value);
    resp.setContentType("text/plain");
    //....
  }
}
```

Срабатывание PVS\-Studio: [V5320](https://pvs-studio.ru/ru/docs/warnings/v5320/) Potentially tainted data in the 'value' variable is used in configuration settings\. This may cause security issues\. MisconfigurationExample\.java 22

В примере выше небезопасно конфигурируются системные свойства\. Практика, при которой пользователь может контролировать приходящие извне данные, является нежелательной, поскольку:

* изменение системных свойств может привести к утечке конфиденциальной информации или её порче\. Самый простой пример — контролируемое пользователем свойство, в котором хранится адрес базы данных\. При изменении имени БД можно получить доступ к данным, раскрытие которых не предусматривалось; 
* некорректно сконфигурированные системные свойства могут привести к падению программы или её нестабильной работе\. К примеру, если пользователю доступно свойство с максимальным количеством потоков, которое может использовать программа\.

В целом, трудно представить ситуацию, где это решает больше проблем, чем создаёт\. Но, если всё же необходимо использовать данные извне для формирования системных свойств, лучше использовать значения из "белого листа":

```cpp
private static final List<String> ALLOWED_VALUES = List.of(
        "value1",
        "value2",
        "value3"
);

@Override
protected void doPost(HttpServletRequest req,
                      HttpServletResponse resp) throws IOException {
  //....
  if (    key == null || value == null || key.isBlank()
      || !ALLOWED_VALUES.contains(value)
  ) {
    resp.sendError(400, "Invalid key or value");
    return;
  }
  //....
}
```



### А06:2021 — Vulnerable and Outdated Components

Выше я говорил, что у нас есть диагностики на 9 из 10 Top Ten категорий\. И пока именно эта категория является той единственной, что нами не покрыта\. 

[**Vulnerable and Outdated Components**](https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components/) — категория, которая содержит в себе лишь одну некорректную с точки зрения безопасности ситуацию: использование в проекте библиотеки или фреймворка версии, содержащей в себе обнаруженную уязвимость\.

Сложность для нас состоит в том, чтобы определить, как лучше агрегировать информацию об уязвимых версиях библиотек\. Как только подобная функциональность у нас появится, мы обязательно об этом расскажем\. Так что обязательно следите за нашими обновлениями и новыми [релизами](https://pvs-studio.ru/ru/whatsnew/)\. И в блоге информация обязательно об этом будет, так что подписывайтесь\.

### A07:2021 — Identification and Authentication Failures

[**Identification and Authentication failures**](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/) — группа уязвимостей, связанных с ошибками в управлении сеансами или аутентификацией пользователей\. Обычно подобные уязвимости способны привести к компрометации паролей, ключей безопасности или токенов сеанса, или позволяют злоумышленнику присвоить себе идентификационные данные\.

Пример:

```cpp
@Configuration
public class CorsConfig {
  @Bean
  public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
      @Override
      public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")          // <=
                .allowedMethods("*")
                .allowCredentials(true);
      }
    };
  }
}
```

Срабатывание PVS\-Studio: [V5325](https://pvs-studio.ru/ru/docs/warnings/v5325/) Setting the value of the 'Access\-Control\-Allow\-Origin' header to '\*' is potentially insecure\. CorsConfig\.java 16

Здесь происходит небезопасная конфигурация CORS\. Выставленное значение `*` заголовку `Access-Control-Allow-Origin` позволяет хосту абсолютно любого происхождения просматривать содержимое ответа кросс\-доменных запросов, совершённых открытой в браузере страницей\. Подробнее про это вы можете прочитать, перейдя [по ссылке](https://pvs-studio.ru/ru/blog/terms/7005/)\. Здесь лишь приведу возможные последствия небезопасной конфигурации:

* раскрытие приватных данных при выходе из корпоративной сети во внешнее интернет\-пространство;
* злоумышленник может выдавать API определённого веб\-приложения за свой, используя данные из браузера атакуемой жертвы \(к примеру, cookies\);
* в случае, если сайт подвержен [XSS](https://pvs-studio.ru/ru/blog/terms/6462/), последствия атаки могут быть более серьёзными\.

И раз уж затронули тему CORS, продемонстрирую ещё один небезопасный паттерн из этой категории:

```cpp
public class NaiveCorsFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, 
                       ServletResponse response,
                       FilterChain chain) throws .... {

    HttpServletRequest req  = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;

    String origin = req.getParameter("origin");

    res.setHeader("Access-Control-Allow-Origin", 
                  origin);
    res.setHeader("Access-Control-Allow-Credentials", 
                  "true");
    res.setHeader("Access-Control-Allow-Methods", 
                  "GET,POST,PUT,DELETE");
    res.setHeader("Access-Control-Allow-Headers", 
                  "Content-Type,Authorization");

    chain.doFilter(request, response);
  }
}
```

Срабатывание PVS\-Studio: [V5323](https://pvs-studio.ru/ru/docs/warnings/v5323/) Potentially tainted data in the 'origin' variable is used to define the 'Access\-Control\-Allow\-Origin' header\. NaiveCorsFilter\.java 25, 22

Здесь CORS\-заголовок формируется на основе данных, пришедших извне\. Возможные негативные последствия такие же, как и в примере выше\. И в обеих ситуациях лучшей практикой будет проверка на соответствие одному из разрешённых доменов:

```cpp
private static final List<String> ALLOWED_HOSTS = List.of(
  "https://allowed-host1.com",
  "https://allowed-host2.com",
  "https://allowed-host3.com"
);

@Override
public void doFilter(ServletRequest request, 
                     ServletResponse response,
                     FilterChain chain) throws .... {
  HttpServletRequest req  = (HttpServletRequest) request;
  HttpServletResponse res = (HttpServletResponse) response;
  
  String origin = req.getParameter("origin");

  if (ALLOWED_HOSTS.contains(origin)) {
    res.setHeader("Access-Control-Allow-Origin", 
                  origin);
  } else {
    res.setHeader("Access-Control-Allow-Origin", 
                  ALLOWED_HOSTS.getFirst());
  }
  ....
}
```

### A08:2021 — Software and Data Integrity Failures

[**Software and Data Integrity Failures**](https://owasp.org/Top10/A08_2021-Software_and_Data_Integrity_Failures/) — группа уязвимостей, приводящих к нарушению целостности программного обеспечения\. Обновления без цифровой подписи, небезопасная десериализация, загрузка зависимостей из небезопасных репозиториев и т\.д\.

Пример:

```cpp
public class UnsafeDeserializeServlet extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest req,
                        HttpServletResponse res) throws .... {

    try (InputStream in = req.getInputStream();
         ObjectInputStream ois = new ObjectInputStream(in)   // <=
    ) {
      Object obj = ois.readObject();       
      // ....
    } catch (ClassNotFoundException e) {
      throw new ServletException("Unknown class during deserialization", e);
    }
  }
}
```

Срабатывание PVS\-Studio: [V5333](https://pvs-studio.ru/ru/docs/warnings/v5333/) Possible insecure deserialization vulnerability\. Potentially tainted data in the 'in' variable is used to deserialize an object\. UnsafeDeserializeServlet\.java 19, 18

Здесь продемонстрирован пример небезопасной десериализации\. Поток байтов берётся из запроса, и на основе их объект восстанавливается\. Как вы можете видеть, никаких проверок не происходит\. Ни что это за объект, ни разрешён ли он для десериализации\. Это открывает пространство для [RCE](https://encyclopedia.kaspersky.ru/glossary/remote-code-execution-rce/)\-уязвимости\. 

Десериализация данных пользователя через нативные Java\-механизмы не лучшая практика\. Тем не менее, если это всё же необходимо, один из вариантов обезопасить себя — проверить, объект какого класса нам приходит и есть ли он в списке тех, что допустимо десериализовывать:

```cpp
class AllowedClass1 { }
class AllowedClass2 { }

class SecureObjectInputStream extends ObjectInputStream {
  List<String> allowedClasses = List.of(
    AllowedClass1.class.getName(),
    AllowedClass2.class.getName()
  );
  
  public SecureObjectInputStream(InputStream in) throws .... {
    super(in);
  }
  
  @Override
  protected Class<?> resolveClass(ObjectStreamClass osc) throws .... {
    if (!allowedClasses.contains(osc.getName())) {
      throw new InvalidClassException("Unauthorized deserialization", 
                                      osc.getName());
    }
    return super.resolveClass(osc);
  }
}

public class DeserializeServlet extends HttpServlet {
  @Override
  protected void doPost(HttpServletRequest req,
                        HttpServletResponse res) .... {
    try (InputStream in = req.getInputStream();
         ObjectInputStream ois = new SecureObjectInputStream(in)
    ) {
      Object obj = ois.readObject();
      // ....
    } catch (ClassNotFoundException e) {
      throw new ServletException("Unknown class during deserialization", e);
    }
  }
}
```

### A09:2021 — Security Logging and Monitoring Failures

[**Security Logging and Monitoring Failures**](https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/) объединяет в себе все ситуации, которые могут:

* помешать диагностировать проблему из\-за некорректной системы логирования;
* привести к раскрытию информации из логов третьим лицам;
* привести к инъекции через систему логирования\.

Пример:

```cpp
public class AccessLogFilter extends OncePerRequestFilter {
  private static final Logger 
                       log = LoggerFactory.getLogger(AccessLogFilter.class);

  @Override
  protected void doFilterInternal(HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) throws .... {

    String ip     = request.getRemoteAddr();
    String method = request.getMethod();
    String uri    = request.getRequestURI();
    String qs     = request.getQueryString();

    log.info("Access {} {}{}?{} from {}", 
             method, uri, "?", 
             qs,   // <= 
             ip);

    filterChain.doFilter(request, response);
  }
}
```

Срабатывание PVS\-Studio: [V5319](https://pvs-studio.ru/ru/docs/warnings/v5319/) Possible log injection\. Potentially tainted data in the 'qs' variable is written into logs\. AccessLogFilter\.java 33, 29

Здесь пример, который показывает, как можно через обычные запросы засорять логи и внедрять в них ложную информацию\. Сам запрос может содержать символы, которые в URL\-кодировке представляют собой символ переноса строки `\n`\. 

Если изначальный URL\-запрос был следующим:

```cpp
/products?category=books%0AERROR:+Inventory+breach+detected
```

То строка `qs` будет равна следующему:

```cpp
category=books\nERROR: Inventory breach detected
```

Как следствие, сообщение в логе будет выглядеть так:

```cpp
INFO  Access GET /products?category=books from 10.1.1.5
ERROR: Inventory breach detected
```

Вторая строка выглядит как сообщение, сгенерированное логгером\. При целенаправленной атаке таких ложных сообщений может быть много\. И в случае возникновения проблемы, из\-за обилия ложной информации её диагностирование и исправление может быть весьма проблематичным\. 

Для того, чтобы не допустить подобной ситуации, необходимо чистить информацию, которая поступает извне и попадает в логи\. Очистку данных можно реализовать самостоятельно, а можно воспользоваться готовыми библиотечными методами\. К примеру, методами класса [`org.apache.commons.text.StringEscapeUtils`](https://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/StringEscapeUtils.html)\.

### A10:2021 — Server\-Side Request Forgery \(SSRF\)

[**Server\-Side Request Forgery**](https://owasp.org/Top10/A10_2021-Server-Side_Request_Forgery_%28SSRF%29/) \(**SSRF**\) — уязвимость, при которой сервер осуществляет подключение к удалённому ресурсу без его предварительной проверки\. Цели у подобной атаки могут быть разными:

* раскрытие информации о внутренней инфраструктуре атакуемого ресурса или организации;
* кража конфиденциальных данных;
* осуществление вредоносных запросов от имени атакуемого сервера\.

Пример:

```cpp
@RestController
public class PreviewController {
  @GetMapping("/fetch")
  public String fetchUrl(@RequestParam("url") String url) throws Exception {
    URL external = new URL(url);
    HttpURLConnection conn = (HttpURLConnection) external.openConnection();
    conn.setConnectTimeout(2000);
    conn.setReadTimeout(2000);

    BufferedReader br = new BufferedReader(new InputStreamReader(
                                               conn.getInputStream()
                                           ));
    StringBuilder sb = new StringBuilder();
    String line;

    while ((line = br.readLine()) != null) {
      sb.append(line).append('\n');
    }
    br.close();

    return sb.toString();
  }
}
```

Срабатывание PVS\-Studio: [V5334](https://pvs-studio.ru/ru/docs/warnings/v5334/) Possible server\-side request forgery\. Potentially tainted data in the 'external' variable is used to access a remote resource\. PreviewController\.java 17, 15

Из запроса получаем URL, к которому без предварительной проверки сразу осуществляется подключение\. Здесь возможно раскрытие внутренней информации, если адрес будет, к примеру, следующим: `http://127.0.0.1:8080/admin`\.

Возможны и другие примеры, где уязвимость осуществляется с другими целями\. Общим для них остаётся вариант защиты — проверка пришедшего извне адреса:

```cpp
private static final List<String> ALLOWED_URLS = List.of(
  "https://allowed-domain1.com",
  "https://allowed-domain2.com"
);
@GetMapping("/fetch")
public String fetchUrl(@RequestParam("url") String url) throws Exception {
  if (!ALLOWED_URLS.contains(url)) {
    //....
    return "Invalid url";
  }
  ....
}
```

## Итог

Мы кратко рассмотрели, что из себя представляют категории OWASP Top Ten на простых примерах\. И параллельно с этим одним глазком взглянули на SAST возможности нашего Java анализатора\.

Безусловно, работа над анализатором продолжается, и количество/качество диагностик улучшается с каждым релизом\. Лучший вариант рассмотреть возможности нашего анализатора — попробовать [самому](https://pvs-studio.ru/ru/pvs-studio/try-free/)\.

Если у вас есть мысли после прочтения этой статьи, которыми вы хотите поделиться, добро пожаловать в комментарии\. С радостью с вами пообщаемся\.

А на этом у нас всё\. Следите за нашим блогом, и до скорых встреч\.