• Блог
  • Небезопасная сессия

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

Многие разработчики, использующие MODX, никогда не задумывались, как работает механизм аутентификации. Т.е. каким образом MODX запоминает залогиненного пользователя и не просит залогиниться при каждом запросе? Оно и понятно. У разработчика своя задача. Он не должен разбирать внутренности каждой CMS и каждого пакета, которые он использует. А я решил разобраться.

Аутентификация в MODX

Механизм практически не отличается от других систем — id залогиненного пользователя сохраняется в сессии, которая индивидуальна для каждого пользователя. Идентификатор сессии сохраняется в сессионной куки, а сама сессия — в таблице modx_session. С каждым запросом браузер посылает сессионную куку на сервер. А MODX берёт из этой куки идентификатор и загружает данные сессии из таблицы. Делается это в специальном классе modSessionHandler при инициализации MODX. Дальше MODX ищет в сессии специальный ключ, в котором указан id пользователя. Если он есть, то пользователь подгружается в $modx->user.

Информация!

Если пользователь залогинен в админке, а на сайте нет, то MODX подгрузит в $modx->user пользователя из админки. Вот такая фича. А почему её не сделали отключаемой, одному Богу известно. Иногда она мешает.

MODX, например в отличие от Laravel, использует стандартный механизм сессий PHP. И все данные хранятся в специальном массиве $_SESSION. И всё бы хорошо, но в MODX есть одна специфическая особенность. Это контексты. Их как минимум 2 — «mgr» и «web». Собственно, админка и сайт. И сессия для них одна. Т.е. всё, что вы сохраните в сессии на сайте, будет доступно в админке. И наоборот. Это может вызвать определённые коллизии с данными. Но это не самое страшное. Проблема в том, что на сайте можно залогиниться в админке просто указав соответствующий ключ в сессии. Маленькое видео для наглядности.

Пользователя можно указать абсолютно любого. Ну и что вы теперь думаете на эту тему? Это фича или всё-таки потенциальная дыра? А если есть такая возможность, то непременно найдётся и желающий её поюзать. Это может быть это хакер, менеджер с доступом к сниппетам или плагинам или недобросовестный разработчик дополнений. И простой перенос или переименование папки manager не поможет. Достаточно добавить код:

$modx->sendRedirect(MODX_MANAGER_URL);

Заключение

Я считаю, что всё-таки нужно разделять сессии от греха подальше. Я понимаю, что многие юзают данную фичу. Я и сам её использую. Но тут нужно определиться, что важнее — удобство или безопасность. И чем раньше, тем лучше. Иначе может получиться как с последним массовым взломом. Ну предупреждал Евгений Борисов о дыре. Но до него никто её не использовал. Поэтому вроде как и не страшно. Авось обойдётся. А вот не обошлось.

В настоящий момент без доработки разделить сессии не получится. Но решение есть — добавить HTTP аутентификацию. По крайней мере, даже залогинившись, злоумышленник не сможет зайти на страницу админки.

П.С.

Вообще, разработчики MODX переборщили с использованием сессии. В неё записывают различные данные пользователя. Видимо с целью оптимизации. В результате чего возникает проблема — необходимо синхронизировать данные с сессией. В сообществе эту тему (с группами пользователя) поднимал Николай Ланец. Потому что вот как получаются группы пользователя:

public function getUserGroups() {
    $groups= array();
    $id = $this->get('id') ? (string) $this->get('id') : '0';
    if (isset($_SESSION["modx.user.{$id}.userGroups"]) && $this->xpdo->user->get('id') == $this->get('id')) {
        $groups= $_SESSION["modx.user.{$id}.userGroups"];
    } else {
        $memberGroups= $this->xpdo->getCollectionGraph('modUserGroup', '{"UserGroupMembers":{}}', array('UserGroupMembers.member' => $this->get('id')));
        if ($memberGroups) {
            /** @var modUserGroup $group */
            foreach ($memberGroups as $group) $groups[]= $group->get('id');
        }
        $_SESSION["modx.user.{$id}.userGroups"]= $groups;
    }
    return $groups;
}

И если админ добавил пользователя в группу, то пользователь об этом не узнает. Ему нужно разлогиниться и залогиниться заново, чтобы обновить сессию. Тоже самое и с названиями групп, и с группами ресурсов.

1   3183

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

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Выделите опечатку и нажмите Ctrl + Enter, чтобы отправить сообщение об ошибке.