惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
C
Cybersecurity and Infrastructure Security Agency CISA
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
Securelist
S
Security Affairs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 热门话题
博客园 - 三生石上(FineUI控件)
T
Threatpost
T
The Blog of Author Tim Ferriss
C
CERT Recently Published Vulnerability Notes
IT之家
IT之家
P
Palo Alto Networks Blog
Microsoft Azure Blog
Microsoft Azure Blog
Spread Privacy
Spread Privacy
Cyberwarzone
Cyberwarzone
腾讯CDC
L
LangChain Blog
Know Your Adversary
Know Your Adversary
C
CXSECURITY Database RSS Feed - CXSecurity.com
GbyAI
GbyAI
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
T
Tor Project blog
AWS News Blog
AWS News Blog
T
Tenable Blog
NISL@THU
NISL@THU
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Hackread – Cybersecurity News, Data Breaches, AI and More
人人都是产品经理
人人都是产品经理
MongoDB | Blog
MongoDB | Blog
MyScale Blog
MyScale Blog
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
美团技术团队
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Анализируем heap-дампы с прода, не привлекая внимания безопасников
Toparvion (S · 2026-05-06 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение13 мин

Охват и читатели3

Обзор

Heap-дампы JVM – бесценный источник информации при разборе аварий с OutOfMemory и оптимизации производительности. Но вместе с тем они же – потенциальные каналы утечки данных, ведь будучи снятыми с боевого сервиса, дампы уносят в себе всё, с чем работал сервис на момент снимка: логины, пароли (иногда в открытом виде), важные ID и т.п. – словом, всяческие sensitive данные, которые не нужны для анализа, но которые навлекают на получателя дампа серьёзную ответственность и риски. Как этого избежать без ущерба делу – разбираемся под катом.


Для начала возьмём лабораторный, но реалистичный пример – Spring PetClinic REST – бэкендовую версию популярного демо-приложения. Эта версия из коробки включает Spring Security, который, в свою очередь, при каждом запуске генерирует пароль для ограничения доступа к методам API. И хотя пароль там “игрушечный” (логируется в открытом виде на старте приложения), механизм его попадания в память и дальнейшего распространения вполне соответствует реальным кейсам у других конфиденциальных данных, поэтому для примера он нам подходит.

Прежде чем искать варианты решения проблемы, надо её увидеть. Благо, на выбранном примере для этого достаточно только:

  1. Склонировать репозиторий
    git clone https://github.com/spring-petclinic/spring-petclinic-rest.git

  2. Открыть его в OpenIDE или другой среде разработки

  3. Открыть там же терминал (или отдельно) и выполнить в нём:
    ./mvnw spring-boot:run.

Эта команда запустит PetClinic с дефолтными настройками (со встроенной БД).

Дальше снимаем дамп памяти и открываем его для анализа, например, с помощью Eclipse Memory Analyzer Tool (File -> Acquire Heap Dump…):

Фрагмент первого окна Eclipse MAT после открытия дампа

Фрагмент первого окна Eclipse MAT после открытия дампа

Среди этого полумиллиона объектов конфиденциальными данными может оказаться что угодно, поэтому ни в Eclipse MAT, ни в других популярных opensource-инструментах нет волшебной кнопки “Покажи мне все возможные утечки”. Зато в них (в частности, в MAT и VisualVM) есть возможность выполнять SQL-подобные запросы к дампу, выуживая таким образом наиболее подозрительные объекты для проверки.

Основная идея этого подхода в том, что каждый класс представляется как таблица, каждое поле класса – как столбец таблицы, а каждый экземпляр – как её строка. Подробнее об этом можно узнать, например, из моего доклада на Joker 2024.

В данном случае из описания и исходников проекта мы знаем, что Spring Security используется в самом простом режиме, когда все данные для доступа хранятся прямо в памяти, а сами явки-пароли представлены классом org.springframework.boot.autoconfigure.security.SecurityProperties.User:

public static class User {

  /**
  * Default user name.
  */
  private String name = "user";

  /**
  * Password for the default user name.
  */
  private String password = UUID.randomUUID().toString();

Этого достаточно, чтобы составить вот такой OQL–запрос к дампу:

SELECT toString(user.name) AS User, toString(user.password) AS Password
FROM org.springframework.boot.security.autoconfigure.SecurityProperties$User user

Он вернёт логин и пароль, действовавшие на момент снятия дампа:

🎯 Вот от этих данных нам и нужно избавиться перед передачей дампа кому бы то ни было.

Аналогичным образом, опираясь на знания бизнес-логики и внутреннего устройства приложения, можно составить несколько OQL-запросов, точечно проверяющих наличие sensitive данных в дампе.

А дальше нужно понять, как и чем их можно вычистить.

Способ 1. Eclipse MAT.

Раз уж мы упомянули Memory Analyzing Tool, было бы логично прояснить его возможности по устранению конфиденциальных данных в дампах. Возможности такие есть, правда, расположены не в самом очевидном месте – нужно вызвать контекстное меню какого-либо элемента дампа и выбрать Export Snapshot:

Вообще, это довольно мощная фича MAT’а, с помощью которой можно выделять из дампа различные срезы, всячески их преобразовывать и сохранять в виде нового дампа так, будто он изначально был снят таким. Как нетрудно догадаться, на эти рельсы хорошо ложится и обфускация данных. Для неё в диалоге экспорта предусмотрено несколько параметров. Главный из них – redact – поддерживает следующие значения:

  • NONE – не меняет ничего;

  • NAMES – пытается обфусцировать только имена классов, полей и методов. Работает в связке с параметром map (см. ниже). Здесь ключевое слово “пытается”, потому что этот подход сопряжен с кучей условностей и ограничений. Подробнее см. в справке на MAT.

  • BASIC – обнуляет все массивы char, int и byte, а также поля классов с типами char и byte. Это позволяет устранить большинство строковых паролей, а также приватных ключей, которые нередко представлены объектами BigInteger. Но этот режим оставляет нетронутыми поля всех остальных примитивных типов, а также их массивов, что может быть как полезно для анализа, так и опасно с т.з. утечек. Если же безопасность превыше всего, то есть вариант:

  • FULL – обнуляет вообще все поля и массивы, а заодно и false-ифицирует все boolean'ы. Щадит только ссылки на объекты и размеры массивов, чтобы не ломать структуру дампа и сохранить его пригодность для анализа.

Второй релевантный нам параметр – map – указывает на properties-файл, в который прописываются соответствия между оригинальными именами классов приложения и их обфусцированными версиями. Это может пригодиться, если вы собираетесь отдать дамп на анализ внешнему подрядчику и не хотите, чтобы он знал, чем занимается ваше приложение. Eclipse MAT генерирует этот файл сам, но если у вас есть лишний рабочий день и вы хотите наверняка сбить с толку вероятного противника, можете прописать эти сопоставления сами: для рассматриваемого примера их понадобится всего 77,5 тысяч. Столько набегает за счёт того, что в маппинг также попадают имена статических полей, а ещё сгенерированные и анонимные классы (включая лямбды). Выглядит это примерно так:

org.springframework.boot.web.server.WebServer = xod.kloblaiwaiwreen.quak.lur.thiarm.Seotreure
org.jspecify.annotations.NonNull = xod.sheopluy.pouprukliel.Trawuak
org.h2.engine.User@passwordHash = xod.gj.exiosy.Knud@tuhoucudruag
jdk.proxy3.$Proxy105@m9 = vuz.briarb.$Fliwoorm@EI
io.swagger.v3.core.util.PrimitiveType@NUMBER = ar.gioliob.is.wras.kind.Graclanaiflob@KREART
org.apache.logging.log4j.MarkerManager$$Lambda = xod.froark.pooniek.luand.Cequiayuaniats$Klourt

Чтобы не перестараться и не сломать парсинг самому себе, Eclipse MAT по умолчанию исключает из обфускации классы пакетов java.* и даёт исключить другие через параметр skip (регулярное выражение).

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

  1. Открыть обфусцированный дамп в MAT

  2. Снова выбрать пункт Export Snapshot

  3. Указать путь для сохранения восстановленного дампа

  4. Указать путь к properties-файлу с маппингом (он будет только читаться)

  5. Поставить галочку undo (параметр redact должен остаться в NONE)

Таким образом можно не хранить у себя исходную копию дампа с оригинальными именами классов, а при необходимости воссоздать её из обфусцированной версии, используя файл маппинга в качестве своеобразного “ключа восстановления”. Только важно помнить, что этот финт не годится для полей/массивов/примитивов внутри классов, так как если они были обфусцированы режимами redact=BASIC или FULL, то этот фарш назад уже не провернуть…

Здесь пора вспомнить, что наша основная задача – вычистить пароль Spring Security, а не засекретить все имена классов, поэтому проверим наличие пароля в дампе тем же OQL-запросом после обфускации в режиме redact=BASIC. Вот что он вернёт:

Как видно, здесь теперь царит безопасность…

В целом, неплохо, но смущает, что для экспорта дампа нам потребовалось открыть его исходную версию и что-то поделать с ней руками, а значит, мы могли увидеть в ней “лишние” данные, что уже влечёт за собой риски, ответственность и вот это всё. К счастью, Eclipse MAT поддерживает т.н. Batch Mode – возможность выполнять некоторые операции без пользовательского ввода, то есть через CLI. Входной точкой этого режима является скрипт ParseHeapDump.[sh|bat] в корневой директории MAT. Как подсказывает имя, изначально скрипт предназначен для автономного парсинга (больших) дампов, но его же можно использовать и для других задач, указывая их имена в качестве аргументов. Для экспорта дампа с обфускацией это может выглядеть примерно так:

./ParseHeapDump.sh plain.hprof -output=redacted.hprof -redact=BASIC org.eclipse.mat.hprof:export

Здесь же можно указывать и другие опции из диалога Export Heap Dump…, например, файл маппинга имен классов -map=myheapdump2.map. А если и этого мало, то можно вызвать всё это программно через Memory Analyzer API и докрутить нужные поведения уже на уровне Java.

Скрытый текст

Для тех, у кого приведенные здесь манипуляции вызвали больше вопросов, чем понимания, или кто в принципе мало знаком с дампами и их анализом в Eclipse MAT, есть специальный тренинг, где можно не только разобраться с этой темой в теории, но и закрепить знания на практике с лабораторным приложением.

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

  • для работы с дампом Eclipse MAT должен его распарсить, а для этого ему нужно, в среднем, столько же оперативной памяти, сколько весит сам дамп;

    • как парсить большие дампы при малом объёме RAM можно почитать в этой заметке.

  • при парсинге MAT производит много вспомогательных индексов, размер которых может в разы превосходить размер исходного дампа, поэтому нужно много места на диске;

    • при этом удалить исходный дамп после парсинга нельзя – он всё равно остаётся частью общей структуры данных;

  • парсинг состоит из множества этапов, включающих многократные обходы исходного дампа, поэтому на больших объёмах это может занимать много времени;

    • у автора этих строк был период оптимизации одного сервера (64 GB RAM), когда можно было делать фиксы не чаще раза в день, потому что каждый новый дамп парсился по 4-5 часов (по ночам).

Да и сам Eclipse MAT – тот ещё комбайн: его дистрибутив весит под 100 МБ, поэтому если стоит задача включить его в состав CI/CD в качестве автоматического этапа обфускации дампов, полученных с боевого сервера, то этот инструмент – не лучший выбор.

К счастью, есть альтернативы.

Способ 2. Heap Dump Tool.

Компания PayPal, тесно работая с платёжными данными и имея часть приложений на JVM, вероятно, много раз сталкивалась с необходимостью анализа “опасных” heap-дампов, поэтому разработала для этого специальный инструмент heap-dump-tool и выложила его в open source.

Это CLI-инструмент под конкретную задачу – на вход подаёте исходный дамп, на выходе получаете “дезинфицированный”:

$ java -jar heap-dump-tool.jar sanitize leak.hprof sanitized.hprof
INFO  Application - heap-dump-tool (1.3.4 ca0325a, 2025-11-29T02:48:26.000-0800)
INFO  SanitizeCommandProcessor - Pre-processing ...
INFO  SanitizeCommandProcessor -     String fields to exclude from sanitization: java.lang.Thread#name,java.lang.ThreadGroup#name
INFO  SanitizeCommandProcessor -     Force match String.coder: true
INFO  SanitizeCommandProcessor - Input File: leak.hprof
INFO  SanitizeCommandProcessor - Starting heap dump sanitization ...
INFO  SanitizeCommandProcessor - Input File: leak.hprof
INFO  SanitizeCommandProcessor - Output File: sanitized.hprof
INFO  SanitizeCommandProcessor - Finished heap dump sanitization in 13s

Если на получившемся дампе выполнить тот же OQL-запрос на выявление пароля, то он выдаст примерно следующее:

, т.е. сплошную безопасность.

Однако это не вся картина – на самом деле, по умолчанию Heap Dump Tool очищает только байтовые и символьные массивы, а целочисленные, вещественные и другие значения (в том числе массивы) сохраняет нетронутыми. Это может оставить лазейку для утечки, если, например, в каком-то целочисленном поле хранился персональный ID клиента или сумма денег на его счету (кое-где есть практика хранить денежные суммы в минимальных единицах валюты). Для таких случаев при вызове утилиты нужно добавить флажок --sanitize-byte-char-arrays-only=false (или кратко -s=false), и тогда вообще все примитивы и их массивы пойдут “под нож”.

Такой режим не включен по умолчанию, вероятно, потому, что может усложнить дальнейший анализ. Разберём на примере. Допустим, нам нужно сопоставить данные дампа с метриками ОС или иной внешней телеметрией. Одним из главных “мостиков” для этого послужит PID процесса. И хотя его можно получить разными способами, удобнее всего было бы взять его напрямую из дампа – в случае Spring Boot для этого достаточно выполнить вот такой OQL-запрос:

SELECT OBJECTS s.source
FROM org.springframework.boot.ApplicationInfoPropertySource s

Для исходного (“сырого”) дампа он вернёт HashMap примерно с таким содержимым:

Однако после обфускации этот результат, очевидно, изменится. И если spring.application.version нам не спасти в любом случае (так как это строковое значение), то целостность spring.application.pid целиком зависит от флага --sanitize-byte-char-arrays-only:

  • при значении true (по умолчанию) PID уцелеет

  • а при false содержимое мапы будет примерно таким:

, т.е. предельно безопасным.

Если бы PID был записан в отдельное поле какого-либо класса, его ещё можно было бы спасти опцией --exclude-string-fields (кратко -e), которая говорит обфускатору, какие поля каких классов нужно оставить нетронутыми. По умолчанию такими полями являются только имена потоков и их групп (см. фрагмент лога выше).

У особо занудного внимательного читателя может возникнуть вопрос: “Зачем указывать команду sanitize, если инструмент и так создан для этого?” Дело в том, что он умеет не только обрабатывать готовые дампы, но и снимать их, причём снимать с приложений в контейнерах (в том числе когда сам запущен в контейнере). Для этого нужно сменить команду sanitize на capture, а путь к дампу – на имя целевого контейнера. В этом случае снятый дамп будет лежать внутри целевого контейнера и его нужно будет забрать оттуда руками (если успеете, пока его не прибил какой-нибудь Kubernetes).

Даже не вникая в остальные возможности этого инструмента, уже должно стать ясно, что он хорошо подходит для нашей задачи:

  • вычищает пароли из дампа даже с умолчательными настройками

  • позволяет влиять на степень “дезинфекции”

  • не требует парсинга дампа

  • поставляется одним JAR-файлом (сделан на Spring Boot + PicoCLI)

  • хорошо встраивается в CI/CD за счёт CLI-интерфейса.

Но куда ж без минусов:

  • На огромных дампах инструмент может потребовать приседаний: под капотом он использует обычный BufferedInputStream с размером буфера 100 Мб. И если на небольших дампах этого вполне хватает, то по мере роста их размера можно оказаться перед неприятным выбором: либо дольше ждать при их чтении/записи, либо выделять сильно больше памяти под буфер.

  • В особо упоротых специфичных случаях может потребоваться кастомная логика обфускации, но heap-dump-tool не предоставляет возможностей по её модификации, а форкать его ради этого – затея сомнительная.

Едва ли много кто сталкивается с этими проблемами, но если вдруг, то вот вариант и на этот случай:

Способ 3. Hprof-redact

Широко известный в узких кругах JVM-спец Johannes Bechberger является автором множества полезных утилит для анализа и оптимизации производительности. И недавно с подачи другого известного в Java мире эксперта Volker’а Simonis’а он выпустил hprof-redact – инструмент для обфускации дампов памяти перед передачей на анализ.

Инструмент во многом похож на описанный выше heap-dump-tool:

  • тоже CLI

  • тоже поставляется одним JAR-архивом

  • тоже предлагает разные режимы редактирования данных.

Но есть и значимые отличия:

  • по заявлению автора, это пока только прототип (proof-of-concept), а не полноценный инструмент;

  • читает дампы в поточном режиме:

    • из плюсов: не требует тюнинга буфера для больших дампов

    • из минусов: делает два полных прохода по дампу, поэтому работает всё равно не мгновенно;

  • управляет редактированием через т.н. трансформеры, коих из коробки в версии 0.2.1 (февраль 2026) было три:

    • zero (умолчательный) – обнуляет все примитивы и заменяет содержимое строк нулями;

    • zero-strings – тоже заменяет содержимое строк нулями, но оставляет примитивы нетронутыми;

    • drop-strings – делает все строки пустыми (нулевой длины), но оставляет примитивы нетронутыми.

Наличие этих трансформеров, к слову, является главным дизайнерским отличием hprof-redact от вышеупомянутого heap-dump-tool, поскольку трансформер – это часть публичного API инструмента, что позволяет при необходимости написать собственную логику обфускации, имплементировав развесистый, но полностью опциональный интерфейс HprofTransformer.

Отдельно для hrpof-redact стоит упомянуть сжатие дампа при обработке. Оно есть и в других инструментах, но Eclipse MAT и heap-dump-tool реализуют его обычным GZip-ом, а у hprof-redact иной подход –  он опустошает строки и примитивные массивы, сохраняя при этом информацию об их исходной длине. В этом смысле такое сжатие не совсем честно, ведь оно теряет данные без возможности восстановления. Однако и цель у него иная – оно призвано сократить объём дампа, сохранив его структурную, но не содержательную идентичность.

В отличие от heap-dump-tool, утилита hprof-redact по умолчанию вычищает не только строки, но и примитивы (включая их массивы), поэтому оба приведенных выше OQL-запроса (для выявления пароля и получения PID+версии приложения) вернут большое… ничего. Всё на радость отделу ИБ.

В целом, hprof-redact пока сложно считать полноценным решением, поскольку он создан совсем недавно и пока что предоставляет не особо много возможностей. Однако он может стать достойной альтернативной тому же heap-dump-tool в случаях, когда требуется особая логика обфускации данных в дампе и вы готовы прописать её программно.

Способ 4. JDK (нет)

Прежде чем рассматривать какие-то сторонние инструменты, было бы логичнее поинтересоваться, а нет ли чего-то готового в стандартной библиотеке? Ответ: сейчас нет, но обсуждения ведутся.

В JIRA проекта OpenJDK есть тикет JDK-8337517, в котором предлагается доработать утилиту jcmd, а также добавить новую JVM-опцию -XX:+HeapDumpRedacted, чтобы и та, и другая на выходе давали дезинфицированный дамп с занулёнными примитивами. Под этот тикет также есть соответствующий Pull Request на Github, однако он в статусе Closed, а сам тикет хоть и имеет статус Open, но поле Fix Version(s) у него имеет значение tbd, т.е. “когда-нибудь”.

Основными причинами такого расклада стали невысокий приоритет задачи и отсутствие (пока) у разработчиков и ревьюеров единого взгляда на то, как должна быть имплементирована эта фича: что именно нужно очищать, какими должны быть аргументы вызова и как быть с тем, что если JVM выдаст обфусцированный дамп, то взять оригинальный будет уже негде. В этом обсуждении участвует и небезызвестный нам Алексей Шипилёв, комментируя в свойственной себе манере:

… while most of the time the confidential data is in primitive arrays (key material, cipher buffers, string contents), primitive fields carry identifiable data as well, e.g. numeric account/transaction IDs. Even double/floats contain data often, think financial data or even (pants heavily) LLM weights.

hprof-redact является попыткой имплементировать эту фичу по исходной (авторской) задумке. Насколько (не)удачной она получилась – судить, в том числе, нам с вами, её пользователям, поэтому если у вас есть опыт её использования и соображения на его счёт, не стесняйтесь делиться ими, например, на странице разработчика или через GitHub issues.

Заключение

Для дезинфекции дампов перед их анализом существует не так уж много бесплатных инструментов. А основных среди них и вовсе только два:

  • Eclipse MAT – мощный, но тяжелый; требует многих приседаний, но предоставляет максимальную свободу действий (GUI / CLI / API);

  • heap-dump-tool – не богатый, зато лёгкий; отлично встраивается в pipeline’ы; умеет не всё, но многое (CLI / API).

Особнячком от них стоит совсем новый и (пока) не глубоко проработанный инструмент hprof-redact, во многом похожий на heap-dump-tool, но делающий ставку на гибкость Java API и поддержку больших дампов.

Чуть более формальное сравнение этих инструментов, во многом основанное на субъективных оценках автора, приведено в этой таблице:

Фича\инструмент

Eclipse MAT

Heap Dump Tool

Hprof-redact

Работа с большими дампами

👍

👍👍

👍👍👍

Поддержка Docker контейнеров

Гибкость настроек обфускации

👍👍👍

👍

👍👍

Сжатие при обфускации

✅ (zip)

✅ (zip)

✅ (hprof)

Гибкость API

👍👍👍

👍

👍👍

Загрузка дампов в формате TAR

Пригодность для CI/CD

👍

👍👍

👍👍

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

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.