Двухфакторная аутентификация для админки — одна из немногих мер безопасности с очевидно положительным соотношением затрат и эффекта: включается за час, а закрывает самый распространённый сценарий компрометации — вход по утёкшему или подобранному паролю.
Разберём настройку и, отдельно, границы того, что 2FA реально защищает: у неё есть слабые места, и знать их нужно до того, как вы решите, что вопрос закрыт.
Зачем 2FA
Пароль — единственный барьер между злоумышленником и вашей админкой. Если пароль утёк (фишинг, брутфорс, утечка базы другого сервиса), сайт скомпрометирован.
Двухфакторная аутентификация — это второй барьер помимо пароля: одноразовый код, который есть только у владельца телефона.
По статистике Google, 2FA блокирует 100% автоматизированных атак, 96% фишинговых атак и 76% таргетированных атак.
Встроенная 2FA в Битрикс
Битрикс поддерживает OTP (One-Time Password) из коробки начиная с версии 14.5.
Включение для администраторов
Шаг 1: Включаем модуль
Админка → Настройки → Настройки продукта → Модули → Проактивная защита
Убедитесь, что модуль security установлен и активен.
Шаг 2: Настраиваем OTP
Админка → Настройки → Проактивная защита → Одноразовые пароли
☑ Включить одноразовые пароли
☑ Обязательное использование для группы: Администраторы [admin]
Количество дней отсрочки: 3Шаг 3: Пользователь настраивает свой OTP
При следующем входе администратор увидит форму настройки:
- Установить приложение-аутентификатор (Google Authenticator, Authy, 1Password)
- Отсканировать QR-код
- Ввести проверочный код
- Сохранить резервные коды
Приложения-аутентификаторы
| Приложение | Платформы | Особенности |
|---|---|---|
| Google Authenticator | iOS, Android | Простое, без облака |
| Authy | iOS, Android, Desktop | Облачный бэкап, мультидевайс |
| 1Password | Все | Интеграция с менеджером паролей |
| Microsoft Authenticator | iOS, Android | Интеграция с MS-аккаунтом |
Google Authenticator не делает бэкапов. При потере телефона — потеря доступа. Используйте Authy или сохраняйте резервные коды.
Настройка через код
Принудительное включение 2FA
// /local/php_interface/init.php
use Bitrix\Main\EventManager;
// Требуем 2FA для всех администраторов
EventManager::getInstance()->addEventHandler(
'main',
'OnAfterUserAuthorize',
function ($arParams) {
global $USER;
if (!$USER->IsAuthorized()) {
return;
}
// Проверяем, администратор ли это
if (!$USER->IsAdmin()) {
return;
}
// Проверяем, настроен ли OTP
$otp = \Bitrix\Security\Mfa\Otp::getByUser($USER->GetID());
if (!$otp || !$otp->isActivated()) {
// Перенаправляем на настройку OTP
LocalRedirect('/bitrix/admin/security_otp.php?init=Y');
}
}
);Проверка статуса 2FA
use Bitrix\Security\Mfa\Otp;
function isOtpEnabled(int $userId): bool
{
$otp = Otp::getByUser($userId);
return $otp && $otp->isActivated();
}
function requireOtpForAdmins(): void
{
global $DB;
// Получаем всех администраторов без 2FA
$admins = $DB->Query("
SELECT u.ID, u.LOGIN, u.EMAIL
FROM b_user u
JOIN b_user_group ug ON u.ID = ug.USER_ID
WHERE ug.GROUP_ID = 1
AND u.ACTIVE = 'Y'
AND u.ID NOT IN (
SELECT USER_ID FROM b_security_otp WHERE ACTIVE = 'Y'
)
");
$withoutOtp = [];
while ($admin = $admins->Fetch()) {
$withoutOtp[] = $admin;
}
if (!empty($withoutOtp)) {
// Отправляем уведомление или логируем
\CEventLog::Add([
'SEVERITY' => 'WARNING',
'AUDIT_TYPE_ID' => 'SECURITY_OTP',
'MODULE_ID' => 'security',
'DESCRIPTION' => 'Администраторы без 2FA: ' . json_encode($withoutOtp),
]);
}
}API для работы с OTP
use Bitrix\Security\Mfa\Otp;
use Bitrix\Security\Mfa\OtpAlgorithm;
// Создание нового OTP для пользователя
function setupOtpForUser(int $userId): array
{
// Генерируем секретный ключ
$secret = Otp::generateSecret();
// Создаём объект OTP
$otp = new Otp();
$otp->setUserId($userId);
$otp->setSecret($secret);
$otp->setType(OtpAlgorithm::TOTP); // Time-based OTP
// Сохраняем (но не активируем)
$otp->save();
// Генерируем QR-код
$qrCodeUrl = $otp->getProvisioningUri(
'Битрикс Админка', // Название сервиса
$userId // Идентификатор аккаунта
);
// Генерируем резервные коды
$backupCodes = [];
for ($i = 0; $i < 10; $i++) {
$backupCodes[] = Otp::generateRecoveryCode();
}
return [
'secret' => $secret,
'qr_url' => $qrCodeUrl,
'backup_codes' => $backupCodes,
];
}
// Проверка OTP-кода
function verifyOtpCode(int $userId, string $code): bool
{
$otp = Otp::getByUser($userId);
if (!$otp) {
return false;
}
return $otp->verify($code);
}
// Активация OTP после первой успешной проверки
function activateOtp(int $userId, string $code): bool
{
$otp = Otp::getByUser($userId);
if (!$otp || $otp->isActivated()) {
return false;
}
if ($otp->verify($code)) {
$otp->activate();
$otp->save();
return true;
}
return false;
}Резервные коды
Резервные коды — страховка на случай потери телефона. Каждый код одноразовый.
Генерация резервных кодов
use Bitrix\Security\Mfa\Otp;
function generateBackupCodes(int $userId, int $count = 10): array
{
$codes = [];
for ($i = 0; $i < $count; $i++) {
$codes[] = Otp::generateRecoveryCode();
}
// Сохраняем хэши кодов в БД
$otp = Otp::getByUser($userId);
if ($otp) {
$otp->setRecoveryCodes($codes);
$otp->save();
}
return $codes; // Показываем пользователю один раз
}Хранение резервных кодов
// Структура таблицы b_security_otp_recovery
// USER_ID | CODE_HASH | USED | DATE_USED
function useBackupCode(int $userId, string $code): bool
{
global $DB;
$codeHash = hash('sha256', $code);
$result = $DB->Query("
UPDATE b_security_otp_recovery
SET USED = 'Y', DATE_USED = NOW()
WHERE USER_ID = {$userId}
AND CODE_HASH = '{$DB->ForSql($codeHash)}'
AND USED = 'N'
");
return $DB->AffectedRowsCount() > 0;
}Рекомендация: храните резервные коды в менеджере паролей (1Password, Bitwarden) или в защищённом облачном хранилище.
Кастомная форма входа с 2FA
// /local/templates/main/components/bitrix/system.auth.form/.default/template.php
<?php if ($arResult['NEED_OTP']): ?>
<div class="auth-otp-form">
<h3>Введите код из приложения</h3>
<p>Откройте Google Authenticator или другое приложение и введите 6-значный код.</p>
<form method="POST" action="<?= POST_FORM_ACTION_URI ?>">
<?= bitrix_sessid_post() ?>
<input type="hidden" name="AUTH_FORM" value="Y" />
<input type="hidden" name="TYPE" value="OTP" />
<div class="form-group">
<input
type="text"
name="USER_OTP"
placeholder="000 000"
maxlength="6"
pattern="[0-9]{6}"
autocomplete="one-time-code"
inputmode="numeric"
autofocus
required
/>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="OTP_REMEMBER" value="Y" />
Запомнить это устройство на 30 дней
</label>
</div>
<button type="submit" class="btn btn-primary">
Подтвердить
</button>
<a href="?backup_code=Y" class="link-secondary">
Использовать резервный код
</a>
</form>
</div>
<?php else: ?>
<!-- Обычная форма входа -->
<?php endif; ?>Доверенные устройства
Битрикс позволяет «запомнить» устройство на определённый срок.
// Настройка в админке
// Проактивная защита → Одноразовые пароли → Доверенные устройства
// Параметры:
// - Срок действия: 30 дней (по умолчанию)
// - Привязка к IP: да/нет
// - Привязка к User-Agent: да/нетПрограммное управление доверенными устройствами
use Bitrix\Security\Mfa\TrustedDevice;
// Добавить текущее устройство как доверенное
function trustCurrentDevice(int $userId, int $days = 30): void
{
$device = new TrustedDevice();
$device->setUserId($userId);
$device->setDeviceHash(TrustedDevice::generateDeviceHash());
$device->setIp($_SERVER['REMOTE_ADDR']);
$device->setUserAgent($_SERVER['HTTP_USER_AGENT']);
$device->setExpiresAt(new \DateTime("+{$days} days"));
$device->save();
}
// Проверить, доверенное ли устройство
function isDeviceTrusted(int $userId): bool
{
$deviceHash = TrustedDevice::generateDeviceHash();
return TrustedDevice::isTrusted($userId, $deviceHash);
}
// Отозвать все доверенные устройства
function revokeAllTrustedDevices(int $userId): void
{
TrustedDevice::deleteByUser($userId);
}Мониторинг и алерты
Логирование событий 2FA
// /local/php_interface/init.php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'security',
'OnOtpVerifySuccess',
function ($userId) {
\CEventLog::Add([
'SEVERITY' => 'INFO',
'AUDIT_TYPE_ID' => 'OTP_SUCCESS',
'MODULE_ID' => 'security',
'ITEM_ID' => $userId,
'DESCRIPTION' => 'Успешная проверка OTP',
]);
}
);
EventManager::getInstance()->addEventHandler(
'security',
'OnOtpVerifyFail',
function ($userId) {
\CEventLog::Add([
'SEVERITY' => 'WARNING',
'AUDIT_TYPE_ID' => 'OTP_FAIL',
'MODULE_ID' => 'security',
'ITEM_ID' => $userId,
'DESCRIPTION' => 'Неудачная проверка OTP',
]);
// Алерт при множественных неудачах
$failCount = getRecentOtpFailures($userId, 10); // за 10 минут
if ($failCount >= 5) {
sendSecurityAlert("Множественные неудачные OTP для пользователя {$userId}");
}
}
);Итоги
Чеклист внедрения 2FA
- Модуль security установлен и активен
- 2FA обязательна для группы «Администраторы»
- Все админы настроили свои аутентификаторы
- Резервные коды сгенерированы и сохранены
- Логирование событий 2FA включено
- Есть процедура восстановления доступа
Что реально даёт 2FA
Таблицы вида «с 2FA фишинг бесполезен» встречаются часто, но они неточны, а неточность в вопросах безопасности приводит к ложному чувству защищённости. Разберём честно.
| Сценарий | Без 2FA | С 2FA (TOTP) |
|---|---|---|
| Пароль утёк из другой базы | Полная компрометация | Не сработает |
| Подбор пароля | Возможен | Практически бесполезен |
| Пароль подсмотрен или записан | Компрометация | Не сработает |
| Классический фишинг (сбор пароля) | Работает | Не сработает |
| Фишинг в реальном времени | Работает | Работает |
| Перехват сессии после входа | Работает | Работает |
| Вредоносный код на устройстве админа | Работает | Работает |
Три сценария, от которых TOTP не защищает, и их стоит знать.
Фишинг в реальном времени. Поддельная страница входа, которая передаёт введённые логин, пароль и одноразовый код на настоящий сайт немедленно. Код действует около минуты — этого более чем достаточно. Такие наборы инструментов существуют и доступны; TOTP их не останавливает. Останавливают аппаратные ключи (WebAuthn/FIDO2), где подпись привязана к домену и на поддельном сайте не сработает.
Перехват уже установленной сессии. 2FA проверяется при входе; после него работает кука сессии. Украденная кука даёт доступ без всякого второго фактора — отсюда важность HttpOnly, Secure, привязки сессии и разумного времени её жизни.
Скомпрометированное устройство администратора. Если на машине работает вредоносный код, он дождётся легитимного входа и воспользуется готовой сессией.
Это не аргументы против 2FA — она закрывает самый частый сценарий и включать её нужно. Это аргументы против того, чтобы считать вопрос безопасности админки решённым после её включения.
Самое слабое место внедрения — процедура восстановления доступа. Администратор потерял телефон, и дальше есть два плохих варианта: либо восстановление настолько простое, что им может воспользоваться злоумышленник (и тогда 2FA обходится через поддержку), либо настолько сложное, что доступ теряется вместе с телефоном.
Рабочее решение — резервные коды, выданные при подключении и хранящиеся вне того же устройства: распечатанные или в менеджере паролей. Плюс второй администратор с правами сброса — но чтобы он не стал точкой обхода, эта операция должна логироваться и быть заметной.
Проверьте процедуру заранее, на тестовой учётной записи. Обнаруживать, что восстановление не работает, в момент, когда никто не может войти в админку, — плохой опыт.
Частые вопросы
Как включить двухфакторную аутентификацию в Битриксе?
Через модуль проактивной защиты: убедитесь, что модуль security установлен и активен, откройте раздел одноразовых паролей в настройках проактивной защиты и включите OTP для нужных групп. Битрикс поддерживает эту возможность из коробки начиная с версии 14.5. При следующем входе пользователь увидит форму настройки: установить приложение-аутентификатор, отсканировать QR-код, ввести проверочный код и сохранить резервные коды.
Какое приложение-аутентификатор выбрать?
Google Authenticator — простое и без облака, но при потере телефона коды не восстановить. Authy даёт облачный бэкап и работу на нескольких устройствах. Менеджеры паролей вроде 1Password держат коды рядом с паролями, что удобно, но кладёт оба фактора в одно место. Microsoft Authenticator удобен там, где уже используется учётная запись Microsoft.
Что делать при потере телефона с аутентификатором?
Использовать резервные коды — их выдают при настройке, и сохранить их нужно сразу, отдельно от телефона. Без них восстановление доступа означает отключение OTP администратором через админку или напрямую в базе, то есть требует другого администратора или доступа к серверу. Это тот случай, когда подготовка занимает минуту, а её отсутствие стоит рабочего дня.
От чего 2FA не защищает?
От нескольких вещей, о которых стоит знать. Она защищает вход, но не защищает уже открытую сессию: украденная кука сессии работает без второго фактора. Она не помогает против вредоносного кода на сервере — если веб-шелл уже отработал, аутентификация ни при чём. И она обходится через восстановление доступа, если оно устроено слабее самого входа: сброс пароля по почте без второго фактора делает всю конструкцию бессмысленной.
Нужны ли доверенные устройства?
Это компромисс между удобством и безопасностью. Запоминание устройства избавляет от ввода кода при каждом входе, что снижает соблазн отключить 2FA вовсе, но одновременно означает, что украденный ноутбук даёт доступ без второго фактора. Разумный вариант — ограниченный срок доверия и возможность отозвать все устройства одной кнопкой.
Кому включать 2FA в первую очередь?
Администраторам и всем, у кого есть доступ к разделам с персональными данными или деньгами. Массовое включение для всех пользователей сайта обычно приносит больше обращений в поддержку, чем безопасности, а вот учётные записи с правами на изменение кода, настроек и заказов — это ровно те, ради которых мера и вводится. И обязательно настройте уведомления о входах: 2FA снижает вероятность компрометации, но не отменяет необходимости знать о ней.