Одна из тысячи похожих историй.

После известных событий компании моего знакомого пришлось оперативно перейти с удобного зарубежного хостинга на площадку попроще. Площадка была настолько проще, что речь уже не шла о штатном мониторинге, логировании или даже привычных группах безопасности для фильтрации трафика. Это был один из тех переездов, которые не успели спланировать. И вот эти самые группы безопасности и подвели. На новом хостинге не было никакого межсетевого экрана на уровне VPS, и Redis оказался доступен для злоумышленников. Они этим естественно воспользовались. Веб-сервис взломали. Сервис был необходим для разработки и поддержки продукта, который через различные сторонние API агрегировал определенную информацию, а затем выдавал её клиентам по запросу. В какой-то момент данных стало много, и было решено с помощью Redis кэшировать часть запросов. Redis стоял на том же сервере, где запускался веб-сервер и никому в голову не приходило как-то особенно заниматься его безопасностью. Но, как водится, порвалось, там, где тонко.

---

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

root@belowzero273:~# redis-cli -h <ip-address>
10.85.0.52:6379> config set dir /usr/share/nginx/html
OK
10.85.0.52:6379> config set dbfilename redis.php
OK
10.85.0.52:6379> set test "<?php phpinfo(); ?>"
OK
10.85.0.52:6379> save
OK

Подключившись, они сменили путь для директории, где хранится база Redis – указали корень веб-сервера, сменили её название на redis.php, а затем, проверив, что phpinfo() успешно выполняется, положили внутрь этого файла свой веб-шелл. Redis не запускался от имени root, но всё равно было неприятно, и часть внутренних данных в итоге утекла.

Redis здорового человека

Это история про ту самую безопасность, в которой не бывает мелочей. После этого случая я решил собрать лучшие практики о том, как можно обезопасить Redis в одном документе. Получилось 7 тезисов:

1. Сделать Redis доступным только с IP-адреса клиента

Для этого нужно, во-первых, отредактировать файл

sudo nano /etc/redis/redis.conf

 и указать в нём

bind 127.0.0.1

Теперь наш сервер слушает только локальные подключения, и мы можем убедиться в этом:

sudo netstat -lnp | grep redis

Output
tcp        0      0 127.0.0.1:6379          0.0.0.0:*             LISTEN      5815/redis-server 1

2. Использовать межсетевой экран

Необходимо на уровне самого сервера добавить правило по фильтрации портов.

sudo ufw status

Output
Status: active

To                         Action      From
--                         ------      ----
6379/tcp                   ALLOW       Anywhere


Не стоит полагаться только на какое-то внешнее решение. Межсетевой экран на уровне самого сервера – это необходимость.

3. Установить пароль

Да, у Redis нет каких-то продвинутых механизмов аутентификации, но даже теми, что есть, пренебрегать не стоит. В конфигурационном файле можно найти и раскомментировать ключ:

# requirepass foobared

А затем вместо foobared указать свой пароль. При этом, в комментарии к этому ключу указано, что, поскольку Redis штука довольно быстрая (а защиты от перебора паролей в ней нет), то 150к паролей можно перебрать за секунду. Соответственно, пароль должен быть длинным и сложным. В общем-то, как и любой другой пароль.

4. Переименовать опасные команды

Redis позволяет переименовывать или даже совсем отключать отдельные команды. Делать это имеет смысл с теми командами, которые условно можно считать опасными, вроде FLUSHALL или SHUTDOWN. Для отключения, например, команды FLUSHALL в конфигурационный файл нужно добавить:

rename-command FLUSHALL ""

А если вы хотите переименовать команду, синтаксис будет выглядеть так:

rename-command FLUSHDB  "pleaseflushmydbiknowwhatimdoing"

Конечно, это классический Security by obscurity, но это тоже мера, которой можно пользоваться на боевых системах, чтобы сбить с толку потенциальных злоумышленников.

5. Включить TLS

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

6. Не запускать Redis от имени root

У Redis нет никаких задач, которым даже в теории потребовался бы доступ уровня root, поэтому и давать его для упрощения жизни не стоит. Но вы это и так знаете и, конечно же, так не делаете, правда?

7. Использовать ACL

Да, в Redis есть Access Control List’ы. Если у вас сложная конфигурация, где к Redis имеет доступ несколько разных пользователей, то хорошей идеей будет использовать ACL. В ACL можно указать к каким командам или к каким ключам у пользователя есть доступ. Например, давайте создадим нового пользователя:

> ACL SETUSER gisuser
OK

Теперь проверим, что может делать наш новый пользователь:

> ACL LIST
1) "user gisuser off &* -@all"
2) "user default on nopass ~* ~& +@all"

По умолчанию нашему пользователю не задан пароль, и он не имеет доступа ни к командам, ни к каким-либо ключам. Строго говоря, такой пользователь сейчас абсолютно бесполезен. Давайте зададим ему пароль и дадим доступ к команде GET, которую можно будет применить к любым ключам, которые начинаются с «cached».

> ACL SETUSER gisuser on >pa$$w0rd ~cached:* +get
OK

Теперь попробуем:

> AUTH gisuser pa$$w0rd
OK
> GET foo
(error) NOPERM this user has no permissions to access one of the keys used as arguments
> GET cached:1234
(nil)
> SET cached:1234 zap
(error) NOPERM this user has no permissions to run the 'set' command

Итак, мы успешно аутентифицировались, посмотрели содержимое ключа 1234 (оно оказалось пустым), затем попробовали задать значение этому ключу, и на это нам не хватило прав.

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

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

Материал подготовил руководитель группы защиты инфраструктурных ИТ-решений компании «Газинформсервис» Сергей Полунин, блог Сергея можно почитать по ссылке.

Комментарии (7)


  1. Shaman_RSHU
    14.12.2022 17:31

    Одна из тысячи похожих историй.

    У каждого зрелого продукта есть рекомендации по настройке безопасности https://redis.io/docs/management/security/

    Просто кто-то заранее всё сделает, но обычно когда "петух клюнет". Очень странная фраза про отсутствие межсетевого экрана. Если это VPS, то почему не поставить iptables/netfilter/.. и дальше открыть наружу только то, что нужно


    1. Gazinformservice Автор
      15.12.2022 09:29

      Действительно можно настраивать iptables, netfilter или что-то подобное непосредственно на уровне VPS. Но на продвинутых хостингах, откуда производилась экстренная миграция, использовались security group'ы, которые управляют доступом к виртуальным машинам, что значительно удобнее, чем настройка индивидуально каждой машины. А вот на новом хостинге аналогичной функции, увы, не было.


      1. Shaman_RSHU
        15.12.2022 10:03

        На провайдера надейся, но сам не плошай... если есть время, ресурсы, необходимость, оно того стоит... :)


  1. olegtsss
    14.12.2022 20:22

    А что случилось-то, как вас взломали? Или вы еще сами не знаете? И не знаете, куда могли еще закинуть свои удочки злоумышленники, когда закрепились на указанном ресурсе?

    Просто мне кажется, вас сбрутили по легкому паролю или инсайдрский слив. А тут (имею ввиду второй пункт) ваших рекомендаций явно будет не достаточно.


    1. Belibak
      14.12.2022 23:32

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


  1. Jsty
    15.12.2022 00:13

    Подход к настройке любого сервиса должен быть nonpermissive.

    Сначала все закрыть и потом открывать по минимуму, чтобы работало.


  1. andreymal
    15.12.2022 01:34
    +1

    1) В Debian и Red Hat редис по умолчанию слушает 127.0.0.1
    2) В Debian и Red Hat редис по умолчанию запускается от имени пользователя redis, а значит у него не будет доступа на запись в /usr/share/nginx/html
    3) В Debian дополнительно ограничивается доступ на запись в каталогах с помощью systemd-опций ReadOnlyDirectories и ReadWritePaths — так что даже root не смог бы записывать куда попало — даже если бы права на каталог стояли 777


    Это означает, что:


    1) Кто-то намеренно включил прослушивание всех интерфейсов
    2) Кто-то намеренно отредактировал юнит-файл systemd, изменив User=redis на User=root
    3) Кто-то намеренно стёр ReadOnlyDirectories и ReadWritePaths в том же юнит-файле, чтобы редис, запущенный под рутом, получил возможность записывать в чужие каталоги


    Как так у вас получилось блин вообще?


    Пофиг на пароль, пофиг на межсетевой экран — у вас там кто-то по сути целую диверсию провёл, изменив безопасные дефолтные настройки