(java || kotlin) && devOps
366 subscribers
6 photos
1 video
6 files
307 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Послушал доклад на JPoint про уже подзабытую уязвимость log4j и удивился 3 вещам.
Напомню: оказывается некоторые версии log4j2 позволяли вставлять в выводимое в лог сообщение выражение, позволяющее сделать удалённый вызов через механизм JNDI lookup. Причём есть возможность не только слить какие-то критичные данные, но и что-то запустить на сервере, пишущем логи, через встроенный в JDK JS движок и его функцию eval.

Первое, что удивило: уязвимость появилась в 2014 году, найдена и обсуждена на конференции по ИБ в 2016 году. Примерно тогда же появился первый фикс, который ничего не фиксил) Выстрелила в конце 2021. Как можно было про нее забыть??? Неразвитость ИБ? Информационная перегрузка? Кому-то было выгодно?

Второе: на первый взгляд эксплуатировать уязвимость сложно - нужно чтобы совпали 3 фактора: использование log4j определённого диапазона версий, отсутствие валидации пользовательского ввода и либо возможность вызвать внешний сервер для передачи конфиденциальных данных или загрузки remote code из внутренней сети, либо сервер атакующего уже должен быть во внутренней сети. Ну и детали об атакуемой системе неплохо бы знать: куда передавать сообщение с exploit-ом, что можно слить, что можно запустить на сервере. Но если подумать, несколько широко распространён log4j, как много пользовательских данных, http заголовков к нам приходит, и что-то из этого точно логируется, а главное как много ИТ систем в мире и 100% не у всех хватает денег на выделенного ИБ спеца - уязвимость совсем не кажется абстрактной, скорее наоборот.

И наконец третье. Разные я видел способы пропатчить библиотеку, сам патчил, но чтобы взять и вырезать файл - это что новое. Но способ эффективный - учитывая редкость использования функционала, в котором была найдена уязвимость, появившийся на сервере ClassNotFoundException в 99,9% случаев будет указывать на атаку. Ну и напоследок - как можно было такое редкий и опасный функционал включать по умолчанию...

Вывод: следование практикам ИБ и feature toggling рулят

#security #vulnerability
Всем привет!

В предыдущем посте я упомянул про защиту от DOS аттак в коде. Раскрою тему.
Для начала стоит различать DDOS и DOS - (Distribted) Denial of Service.
Первый - это когда злоумышленник долбит миллионами запросов в секунду. Такое не выдержит ни один сервис, не поможет даже k8s, т.к. ресурсы кластера не резиновые - https://kubernetes.io/docs/setup/best-practices/cluster-large/ да и подымаются новые ноды не мгновенно. Следовательно, от DDOS должна защищать сетевая инфраструктура, прикладной разработчик тут ничего сделать не может.
Другое дело DOS - RPS на порядки меньше, эксплуатируются уязвимости в коде. Вопрос - откуда злоумышленники про них узнают?
Во-первых они могут действовать наугад, во-вторых - всегда могут быть болтливые сотрудники, а главное - защита типа "об этом никто никогда не узнает" - плохая защиты.
Суть всех уязвимостией при DOS одна - поднять на сервере столько потоков одновременно, чтобы закончилась память или загрузка процессора ушла под 100%
Итак, как можно улучшить код для защиты от DOS.

1) проводить нагрузочное тестирование (НТ). НТ позволяет точно определеить сколько нужно серверов, чтобы держать расчетную и пиковую нагрузку. Пиковую нагрузку можно взять как расчетная умножить на два. Утечки памяти, неоптимальный код - все это с большой вероятностью можно увидеть на НТ

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

2) таймауты должны быть согласованы. Если мы обрабатываем запрос с таймутом 5 секунд, он синхронный, а вызываем смежника с таймутом 10 секунд - мы зря тратим его и свои ресурсы. Согласование может быть ручным, либо можно слать, например в заголовках, свой таймаут смежнику, чтобы он не ждал зря.

3) использовать circuit breaker, он же предохранитель, он же техперерыв. Если известно, что смежная система прилегла - не надо ее добивать и тратить на это свои ресурсы. Берем данные из кэша если это возможно или возвращаем клиенту ошибку. Принцип fail fast. Стоит отметить, что настройку таймаутов, предохранителя, и числа повторов можно делать либо в коде, либо отдать на откуп Istio или аналогичной системе если мы в облаке. Что лучше - это отдельная тема

4) защищаться от уязвимостей типа Injection. Суть их в том, что злоумышленник передает в параметрах входящего запроса что-то, что приводит к нелинейному потреблению ресуросов или тяжелым запросам в БД. Примеры первого вида DTD схемы https://habr.com/ru/post/170333/, регулярки - https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS, второго - SQL Injection со сложными JOIN. Решение: валидация параметров, регулярно сканировать библиотеки на наличие уязвимостей, регулярно обновляться в части хотфиксов

5) логика сервиса не должна линейно зависеть от числа входных параметров либо число параметров должно ограничено. Чем-то похоже на предыдущий пункт, но тут приложение спроектировано криво, поэтому никакие уязвимости не нужны)

6) использовать пулы потоков. Во многих случаях они уже используются - обработка входящих веб-запросов, JDBC запросы к БД. Но есть потоки, которые создаем мы сами. Если на каждый входной запрос мы будем создавать дополнительно хотя бы +1 поток, то это примерно удвоит потребление ресурсов. А если больше одного... Пул потоков защищает от такой ситуации

7) не забывать закрывать ресурсы - файлы, коннекты к БД. Все что java.io.Closeable. И делать это правильно - try with resources. В отличие от памяти в куче ресурсы никто за вас не закроет. А они жрут память и часто ограничены: максимальное число открытых файлов в Linux, максимальное число запросов, которое может обрабатывать СУБД

8) не использовать тяжелые JOIN и GROUP BY запросы к БД. Создавать индексы, смотреть план выполнения запроса. Об этом должен позаботиться ваш DBA, но, увы, не всегда он есть

9) не использовать излишне сильные уровни блокировки в БД, не использовать блокировки файлов без явной необходимости

#code_quality #security #patterns
И снова привет!

По поводу недавней уязвимости в Apache Commons Text https://habr.com/ru/company/pt/blog/694720/ у меня две мысли:
1) техника очень похожа на Log4Shell https://ru.wikipedia.org/wiki/Log4Shell, с которой и начался этот блог - и там, и там интерполяция выражений в текстовых строках с далеко идущими последствиями. Who is next?)))

2) лично я узнал о возможности интерполяции в обоих случаях после появления соответствующих уязвимостей))))

#security #vulnerability #java
8) каждый образ можно пометить тэгом, например, с версией. Но есть нюанс - образ с конкретным тэгом можно перезаписать. Причем это не вопрос прав доступа, а фича Docker. И это несекьюрно. Поэтому нужно использовать хэш образа вместо тэга. Он не задается при сборке, а высчитывается реестром и меняется при изменении содержимого образа. Более продвинутая защита - подпись образа его создателем, для этого есть ряд утилит https://dev.to/snyk/signing-container-images-comparing-sigstore-notary-and-docker-content-trust-1bfm

#docker #security
Всем привет!

Упоминал вскользь вопросы безопасности при использовании Docker https://t.me/javaKotlinDevOps/233
Есть еще один важный момент.
Часто для передачи каких-то стендозависимых данных в Docker сервис используют переменные среды.
Более того, использование переменных окружения рекомендуется в довольно известном манифесте "12 факторов", описывающем принципы разработки облачных приложений, в разделе про конфигурацию: https://12factor.net/ru/config
Кстати, рекомендую почитать его целиком, там все пункты - полезны, хотя некоторые из них кажутся очевидными)

Но как всегда есть нюансы. Не все можно хранить в переменных среды. Когда это лучше не делать? Чтобы ответить на этот вопрос, надо учесть следующие особенности переменных среды:
1) переменные среды наследуются дочерним процессом от родительского. Но в момент наследования связь между ними теряется. Т.е. изменение\добавление переменной в родительском процессе никак не влияет на дочерний. Те, кто меня JAVA_HOME или PATH в работающей системе меня поймут)
2) доступ к переменным среды никак не ограничен. Более того, их часто отображают в UI в качестве справочной информации, иногда они попадают в логи - т.е. легко допустить утечку информации. Да, можно перезаписать значение переменной (удалить нельзя), но см. п.1

Отсюда следует два ограничения:
1) нельзя хранить в переменных среды секреты
2) не стоит хранить данные, которые могут изменится в процессе работы приложения. Т.к. в этом случае pod\контейнер придется перезапускать.

#security
Всем привет!

Когда говорят о том, как необходимо учитывать требования информационной безопасности (ИБ) при разработке ПО часто забывают об одной проблеме.
Мир ИБ и мир разработки часто практически не пересекаются. Специалистов по ИБ нет в командах, разработчики и "безопасники" говорят на разном языке. Поверхность атаки, вектор атаки, SAST, DAST, OSS - для команды это что-то далекое, внешнее. Это плохо, но это факт. А встречаются эти два мира часто перед моментом выхода нового релиза на ПРОМ. Что тоже плохо.

Что же тут можно сделать?
1) базу про ИБ разработка должна знать, немного писал об этом тут https://t.me/javaKotlinDevOps/27
2) представители ИБ должны приходить в команды, в виде митапов, личных встреч, раннего ревью архитектуры, т.к. в любом случае именно они знают наиболее важные болевые точки
3) а так как мы живем в мире победившего Agile - требования ИБ тоже можно внедрять по Agile. Начиная с простых - технических, связанных с конкретными интеграциями и данными. Т.к. именно техническую стороны разработчики знают и могут контролировать. Подключая владельца продукта и специалиста ИБ, т.к. именно они должны знать, где потенциально могут быть наибольшие потери в финансовом и репутационном плане.

И по этому поводу есть хорошая статья, где процесс расписан по шагам https://habr.com/ru/companies/vk/articles/504062/

#security
Всем привет!

Прочитал статью про работу с секретами в Java: https://habr.com/ru/companies/sberbank/articles/870116/
Лично я из статьи подметил три интересных момента:

1) Сейчас много говорят о безопасной разработке. Книги, доклады на конференциях… Что имеем на практике? Вот есть понятная рекомендация — хранить пароли не в String, а в char[]. Так как String — это объект, и его содержимое будет в heap dump до очередной уборки мусора. А уборка мусора проходит в несколько этапов, и принудительно вызвать её мы не можем. А char[] мы можем очистить сразу после использования. Так вот — в статье у нас есть embedded Tomcat, Jersey HTTP client и Hikari pool. Три широко распространённых компонента, требующих секретов при работе. Сколько из них поддерживают передачу секретов в char[]? Увы, только Jersey client. И это уровень фреймворков и библиотек, на бизнес-уровне всё будет ещё хуже.

2) Перегружаемые настройки Spring Cloud, работающие через @RefreshScope и описанные мною ранее, подходят, увы, не всегда. Основная проблема — передача секрета в компоненты, инициализируемые сложно, однократно при старте или некорректно обрабатывающие событие обновления секретов — например, сбрасывающие активные клиентские сессии.

3) Кроме @RefreshScope изобрели ещё два “велосипеда", причём оба в Spring Boot: SSL bundles и Spring Cloud Vault. Первый предназначен для работы с хранилищами сертификатов, второй — для работы с HashiCorp Vault. Оба поддерживают обновление секретов на лету. Все три инструмента взаимодополняют друг друга, хотя и не покрывают 100% кейсов.

#security #spring
Всем привет!

Разбираясь с HashiCorp Vault, понял, что многие, как минимум я, недооценивают его. Что такое Vault? В первую очередь — безопасное хранилище секретов. Аутентификация и авторизация, хранение всех данных в зашифрованном виде. Причём до ввода мастер-пароля администратором само приложение не имеет к ним доступа. Это всё понятно.

Но есть ещё киллер-фича: автогенерация секретов. Архитектура Vault оперирует понятием движка (engine) для работы с различными секретами. Рассмотрим, как ротация сделана для разных движков.

Движок для работы с сертификатами — PKI engine — умеет перегенерировать сертификаты с истекающим сроком. Вот документация: https://www.hashicorp.com/blog/certificate-management-with-vault

Database engine умеет создавать «одноразовых» пользователей в СУБД с помощью фичи под названием dynamic secrets: https://www.hashicorp.com/blog/why-we-need-dynamic-secrets. «Одноразовых» — то есть с ограниченным временем жизни, на один типовой сеанс работы с БД. Причём API Vault позволяет продлить время жизни пользователя для синхронизации с временем сессии. Не уверен, что любая БД выдержит такой режим работы, но видится, что эта функция сильно увеличивает безопасность работы с БД. Может возникнуть вопрос — как Vault их создаёт. ANSI SQL — это хорошо, но диалекты отличаются, да и в конкретной компании могут быть свои правила. Тут всё просто — SQL-запрос для создания пользователя и выдача ему необходимых прав создаются администратором Vault. Естественно, нужно задать логин и пароль администратора СУБД, под которым будут выполняться эти запросы. Но кажется, Vault вполне можно считать безопасным местом для их хранения. Больше деталей здесь: https://www.baeldung.com/vault, а в части интеграции со Spring Vault — здесь: https://www.baeldung.com/spring-cloud-vault.

Также есть возможность ротировать пароли доменных пользователей, используя Active Directory engine — см. https://developer.hashicorp.com/vault/docs/secrets/ad.
И обычные пароли: https://www.hashicorp.com/resources/painless-password-rotation-hashicorp-vault. Странно, что для последнего нужен внешний плагин, но такая возможность есть.

Итого: автоматическая ротация секретов и распространение их с помощью Vault Agent (в виде сайдкаров или JAR-библиотек) выглядят крутой фичей в плане безопасности и упрощения работы администраторов. Наверняка на этом пути будут подводные камни, но путь однозначно верный.

#security #vault #spring
Всем привет!

Возвращаюсь к теме хранения секретов — вот ещё неплохая статья https://habr.com/ru/articles/872128/
показывающая, что если вы «доросли» до вопроса управления секретами, вариантов немного.
Если вы находитесь в коммерческом облаке — Google, AWS, Azure, и скорее всего Яндекс, VK, Сбер — там будет встроенное хранилище секретов, и логично его использовать. Иначе — все дороги ведут в Vault.
Если рассмотреть альтернативы из статьи:
1) Docker Swarm умирает, проигрывая Kubernetes.
2) BuildKit — технология в себе, закрытая в плане сообщества и документации.

#security #vault