ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

Java-зависимости и транзитивный Log4j: начинаем инвентаризацию у клиентов

Security-сообщество активно обсуждает сложность аудита транзитивных Java-библиотек. Мы начали инвентаризацию Log4j в enterprise-стеках клиентов - превентивно.

Контекст момента

Security community активно обсуждает риски Java-зависимостей и сложность аудита транзитивных библиотек в enterprise-приложениях

В последние недели в security-чатах и рассылках неожиданно оживилась тема Java-зависимостей. Не конкретный CVE, не инцидент - просто несколько обстоятельных постов про то, как сложно понять, что именно тащит в себе enterprise Java-приложение. Транзитивные зависимости, вложенные jar-файлы, артефакты, собранные полгода назад и задеплоенные без пересборки. Тема не новая, но разговор пошёл конкретный - с примерами и инструментами. Мы решили, что это хороший повод разобраться, как обстоят дела у наших клиентов.

Начали с самого простого вопроса: какие версии Log4j используются в production? Звучит невинно. На практике - отдельное приключение.

Почему транзитивные зависимости - это проблема именно в Java

В экосистеме npm или PyPI зависимость - это обычно отдельный каталог в node_modules или site-packages, и её версию видно сразу. В Java мир устроен иначе: зависимости упаковываются внутрь jar и war файлов. Fat jar, uber jar, shadow jar - артефакт содержит всё нужное внутри себя, и это удобно для деплоя. Но означает, что для ответа на вопрос «какая версия Log4j?» нужно либо смотреть в pom.xml исходников (если они есть и актуальны), либо лезть внутрь jar и разбирать вложенные архивы.

У enterprise-приложений ситуация ещё веселее. Типичный стек: несколько WAR-файлов на одном Tomcat, каждый из которых собирался независимой командой в разное время, часть - вендорские продукты без исходников. pom.xml к ним может и не прилагаться. Спрашивать разработчиков «а какая у вас Log4j?» - занятие с непредсказуемым результатом.

Что мы используем для инвентаризации

Первый инструмент - jar tf и рекурсивный поиск. Грубо, но работает:

# найти все jar внутри war
jar tf application.war | grep "log4j"

# если упакованы вложенные jar - распаковываем и смотрим внутри
find . -name "*.jar" -o -name "*.war" | while read f; do
  jar tf "$f" 2>/dev/null | grep -i "log4j" | sed "s|^|$f: |"
done

Этот подход находит log4j-*.jar в очевидных местах. Но если Log4j зашит внутрь fat jar как набор классов, а не как отдельный jar - этот метод его не видит.

Второй инструмент - анализ манифестов и pom.properties. Внутри каждого jar, собранного Maven, есть META-INF/maven/<groupId>/<artifactId>/pom.properties с версией. Это надёжнее:

find . -name "*.jar" -o -name "*.war" | while read f; do
  unzip -p "$f" "*/pom.properties" 2>/dev/null \
    | grep -A2 "log4j" | grep version
done

Третий вариант - Syft. После того как мы использовали его для SBOM container-образов, попробовали прогнать по файловым системам серверов напрямую. Syft умеет анализировать Java-архивы, включая вложенные jar:

syft dir:/opt/tomcat/webapps -o table | grep -i log4j

Результат аккуратнее ручного поиска: видно groupId, artifactId, версию, путь к файлу. На стеках с десятком WAR-файлов это ощутимо быстрее.

Что нашли у клиентов

Картина, честно говоря, не удивила, но масштаб немного впечатлил.

Разброс версий. Там, где мы ожидали увидеть более-менее единообразную картину, обнаружили Log4j от 1.2.x до 2.14.x в рамках одного сервера. Log4j 1.x официально не поддерживается с 2015 года и имеет свои известные проблемы, но живёт в вендорских компонентах и никуда не торопится.

Транзитивные зависимости фреймворков. Несколько приложений не используют Log4j напрямую - в их pom.xml его нет. Но Log4j приходит транзитивно через Hibernate, Apache Struts, Spring-компоненты старых версий. Разработчики про это не знают, потому что «мы не подключали Log4j».

Вендорские jar без pom. Два продукта - коммерческие, исходников нет. Версию Log4j удалось установить только через анализ манифеста jar вручную. В одном случае Log4j оказался встроен как классы в общий fat jar, и определить точную версию получилось только через хеш LogManager.class и сверку с известными сборками.

Почему это работа, а не скрипт

Автоматика находит большинство случаев, но не все. Несколько ситуаций, которые требуют ручной проверки:

  • Переименованные пакеты (shading). Maven Shade Plugin умеет переименовывать пакеты внутри jar. org.apache.logging.log4j может оказаться зашейден в com.vendor.thirdparty.log4j. Автоматический поиск по имени это не найдёт.
  • Нестандартные пути развёртывания. Некоторые приложения кладут jar в /var/lib/, /opt/vendor/, /usr/share/java/ - нужно знать инфраструктуру, чтобы не пропустить.
  • Классы без jar. Redeploy на Tomcat иногда оставляет extracted классы в work/ и webapps/<app>/WEB-INF/classes/ - они не в архивах, их нужно искать отдельно.

По этим причинам мы делаем инвентаризацию как аудит, а не как автоматизированный скан: инструменты дают 80%, остаток требует понимания конкретной инфраструктуры.

Где сейчас

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

Сама по себе картина не катастрофическая - Log4j 2.x в основном свежих версий. Но точное знание того, что именно и где установлено, само по себе ценно: когда что-то случается, не нужно срочно бегать по серверам с find. Реестр уже есть.

Выльется ли текущее оживление в security-сообществе во что-то конкретное или останется академическим разговором - неясно. Но инвентаризировать в спокойном режиме приятнее, чем в режиме аврала.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.