Security 26.300.0: xscan ищет файлы-маркеры взлома и снова работает на новом main
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
Security 26.300.0 чинит и усиливает сканер «Поиск троянов» (xscan). Он ищет семь файлов, которые остаются на сайте после взлома, проверяет файлы, чьё имя отличается от системного только регистром, и снова запускается на main 26.900.0 и новее. А на порталах с push-OTP и обязательной 2FA резервный email из security 26.200.0 теперь можно подтвердить ещё до входа.
Новое
Проверьте сайт на файлы-маркеры
Новый метод CBitrixXscan::CheckBadFiles() срабатывает при каждом запуске сканирования, вместе с проверкой событий, агентов и шаблонов. Он ищет в корне сайта такие файлы:
/bitrix/templates/.mod.txt/b_user.json/bitrix/tools/.sess/bitrix/tools/.idSess/.security.log/bitrix/components/bitrix/app.layout/params.php/info_files.tar.gz
Каждая находка попадает в результаты с типом file и сообщением bad file. Маркер в app.layout похож на штатный .parameters.php из того же компонента, но файла params.php в bitrix:app.layout нет.
Список зашит в код модуля, и сверить с ним можно любой сайт, даже где security ещё не обновлён. Скрипт для крона завершается с кодом 1, если нашёл хоть один маркер:
<?php
declare(strict_types=1);
// php check-markers.php /var/www/site
$root = rtrim($argv[1] ?? getcwd(), '/');
$markers = [
'/bitrix/templates/.mod.txt',
'/b_user.json',
'/bitrix/tools/.sess',
'/bitrix/tools/.idSess',
'/.security.log',
'/bitrix/components/bitrix/app.layout/params.php',
'/info_files.tar.gz',
];
$found = array_filter(
$markers,
static fn (string $path): bool => is_file($root . $path),
);
foreach ($found as $path) {
printf("%s\t%s\n", $path, date('Y-m-d H:i:s', (int) filemtime($root . $path)));
}
exit($found === [] ? 0 : 1);
По дате изменения рядом с путём видно, с какого дня искать следы в логах.
Перезапустите сканирование
С main 26.900.0 в ядре лежит php-parser ветки 5.x. У его ParserFactory нет ни create(), ни PREFER_PHP7, а сканер из security 26.200.0 вызывал их в конструкторе, так что на свежем main xscan падал, не начав обход. Теперь он берёт createForHostVersion(), а старый вызов оставлен запасным для php-parser 4 из main 26.800.0 и более ранних версий.
Белый список системных файлов в CBitrixXscan::SystemFile() стал регистрозависимым. Раньше файл, чьё имя отличалось от записи в списке только регистром, считался системным и проверку пропускал, теперь сканер его разбирает. Вызовы Bitrix\Call\Rest\Auth::authorizeById, наоборот, больше не помечаются ни в обработчиках событий ([050] dangerous method at event, check arguments), ни в коде файлов ([307] danger method).
В гриде результатов («Поиск троянов» → «Сканирование файлов») появился пресет фильтра not modified. Он прячет строки [201] file was modified, то есть файлы, которые не совпали с эталонным хешем. Файл, у которого кроме [201] есть другие находки, пресет не прячет. Когда ядро правлено руками, строки [201] забивают грид, и с пресетом проще увидеть остальное. Изменённый файл ядра тоже может оказаться закладкой, так что хотя бы раз просмотрите выдачу без пресета. Подпись у пресета не переведена, в списке он стоит по-английски рядом с not /bitrix/modules.
Резервный email при обязательной 2FA подтверждается до входа
Если на портале 2FA обязательна, пользователь, который её ещё не настроил, после логина и пароля останавливается на шаге OTP. Теперь модуль разрешает ему на этом шаге указать резервный адрес и подтвердить его кодом из письма, ещё не войдя. Для этого с действий security.pushotp.sendConfirmationEmail и security.pushotp.confirmEmail сняли фильтр авторизации.
Неавторизованный запрос проходит, только если выполнено всё сразу:
- запрос пришёл через AJAX;
- в сессии записан отказ во входе из-за обязательной 2FA;
- OTP включён и обязателен;
- пользователь активен, пропустить 2FA не может и ещё её не активировал.
Иначе действие вернёт ошибку OTP_PARAMS. CSRF-проверка осталась. Ещё до этих условий оба действия, как и раньше, проверяют настройки канала. Без опции otp_allow_email они отвечают EMAIL_DISABLED, а если Otp::isPushPossible() возвращает false, то PUSH_OTP_DISABLED. Для push-OTP нужны установленный модуль mobile, модуль pull и включённый сервер очередей.
sendConfirmationEmail принимает email и возвращает signedData и timeLeft. confirmEmail ждёт шестизначный code и тот же signedData. На «Бизнесе» сценарий не заработает и с вашей формой, потому что в нашем эталоне БУС нет модуля mobile и оба действия отвечают PUSH_OTP_DISABLED. Штатные формы входа этого эталона их и не вызывают.
Мелочи и находки
- Защищённый статический
Otp::getCacheDir()заменён наgetCacheId(), который возвращает пару[cacheId, cacheDir], а ключ кеша данных OTP сменился сotp_users_{id}_v1наotp_users_{id}_v2. Метод нужен только наследникамOtp, а их ядро не создаёт:Otp::getByUser()возвращаетnew self. Если чистили кеш OTP по ключу руками, переходите наOtp::cleanCache($userId). - В кеше OTP теперь лежит логин пользователя, а
CSecurityUser::onAfterUserUpdate()сбрасывает этот кеш, только если в полях обновления естьACTIVE. Если ваш код меняет логин черезCUser::Update()безACTIVE, письмо подтверждения адреса может прийти со старым логином, пока кеш не истечёт. По умолчанию он живёт сутки (86400 секунд). - Счётчик редиректов
LOCAL_REDIRECTSпереехал из kernel-сессии вApplication::getLocalSession('security_redirect')и пишется только при запущенной сессии. Если ваш код читал этот ключ, ищите его там. - Модуль ушёл с устаревших
CHTTP::sGet()иCHTTP::isPathTraversalUri(). В своём коде меняйте их на те же(new HttpClient())->get($url)и(new Uri($uri))->isPathTraversal()изBitrix\Main\Web.HttpClientпо умолчанию идёт за редиректами, до пяти переходов, поэтому тест окружения теперь тоже по ним ходит.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
Bitrix\Security\Mfa\Otp::getCacheDir()
|
Удалено |
Bitrix\Security\Mfa\Otp::getCacheId()
|