Кому лень читать можно сразу идти к исходным кодам, страницу загрузки и документацию.  

Предыстория

Многократно сталкиваясь в своей работе с решениями для второго фактора аутентификации, я все больше убеждаясь в их не оптимальности - либо неудобны как Google authenticator, либо дороги как Microsoft authenticator. Соответственно, появлялось желание сделать свой MFA с блэк-джеком, бесплатный и с пуш уведомлениями.

Впереди были майские выходные, и я решил попробовать.

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

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

Было решено не лезть в WinAPI для безопасности и стабильности приложения, а ограничиться только информацией, получаемой из команды quser, чтения EventLog и выполнения команд msg.exe и tsdiscon.exe. Сразу скажу, что получение IP из EventLog оказалось не стабильным и пришлось использовать WinAPI, но только для чтения.

Итак, за праздники первый прототип был готов, и пошло тестирование и допиливание.

Что получилось

ServiceLogonMultiFactor (SLMF) - сервис написанный на С#, .Net 4.0 из-за его распространённости по умолчанию.

Сервис, по событию входа или разблокировки получает ИД сессии, далее через вызов команды quser и WinAPI имя пользователя и IP адрес, если подключение не локально.

Полученную информацию сервис посылает на Телеграм пользователя, с кнопками разрешить/ запретить, и в зависимости от прав пользователя – добавить/удалить этот ИП в список доверенных.

Если получена команда отключить или превышен заданный интервал, пользователь отключается командой tsdiscon с соответствующим сообщением в Телеграм.

За несколько секунд до отключения (параметр задается в конфиге) пользователю на экране начинает выводиться сообщение с помощью msg.exe.

Дополнительно, при наличии прав, пользователь может получить следующую информацию

  1. Список текущих пользователей;

  2. Список задач (топ 10 задач по потреблению памяти для каждого пользователя);

  3. Текущие настройки сервиса;

  4. Информацию из команды systeminfo: аптайм машины и количество свободной памяти;

  5. Информацию о сервисе: версия, аптайм и количество ошибок чтения Телеграм;

Так же (при наличии прав) можно выполнять команду tsdiscon.

На каких версиях тестировался

Как уже упоминалось выше, минимальная версия для теста была windows Server 2008 R2, пока все апдейты не подставились - телеграмм из IE не открывался и сервис не работал. Всего тестировалось:

  • Server 2008R2

  • Server 2012

  • Server2012r2

  • Server2016

  • Server2019

  • Windows 10 English, Russian

 В связи с тем, что используются результаты выполнения команд CMD, пришлось вынести региональные настройки в файл конфигурации. Английскую и Русскую версию можно сравнить:

Сценарии использования и безопасность

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

Если данный сервис используется для защиты персонального компьютера, на котором пользователь работает с правами локального администратора, то тут все сложнее. Пользователь всегда может выполнить команду net stop servicelogonmultifactor и остановить этот сервис. Как дополнительная защита возможна установка сервиса с нестандартным именем и описанием, а также запуск из нестандартного каталога. В этом случае поиск этого сервиса займет очень значительное время, а с учетом отключения сессии каждые 10-15 секунд будет практически невозможным.

Естественно, если на вашем компьютере открыт WinRM или удаленный реестр, то при наличии у злоумышленника имени и пароля никакие вторые факторы не помогут.

Не плохо данный сервис подходит для дополнительной защиты компьютера, на котором находится финансовое ПО, при логине можно посылать сообщение не только пользователю, но и другим сотрудникам, например- безопаснику или директору.

И естественно, если клиент телеграмма стоит на том-же компьютере что и сервис, то задача сильно усложняется, хотя, конечно, можно выполнить команду <имя компьютера> tsdiscon <номер сессии> и отменить авторизацию десктоп клиента.

Текущий статус

На сегодняшний день проект скорее находится в статусе концепции, а не законченного решения.

Главной архитектурной канвой этого проекта было “не навреди”. В результате пострадала функциональность. Для безопасности было ограничено использование команд, которые может выполнить этот сервис https://github.com/Constantine-SRV/ServiceLogonMultifactor/blob/master/ServiceLogonMultifactor/Wrappers/ExecuteCommandWrapper.cs

private const string AllowedCommands = "quser.exe tsdiscon.exe msg.exe systeminfo.exe tasklist.exe";

 В текущей версии решил обойтись даже без PowerShell.

Также в данном проекте для исключения внешних ошибок и прочих недокументированных особенностей не используется никаких сторонних библиотек. Только инсталлятор сделан на WixSharp, но для большей безопасности без него можно обойтись. https://github.com/Constantine-SRV/ServiceLogonMultifactor/wiki/RU-4.-Установка-через-InstallUtil.exe

Спорный вопрос - надо ли блокировать вход пользователя до получения ответа от Телеграм. При всех очевидных достоинствах это решение имеет ряд недостатков или вопросов, например, как организовать резервный вход на случай сбоя сервиса или телеграмма?

Может лучше сделать блокировку пользователя после 2-3 неудачных входов и последующую разблокировку по команде с телефона?

Как совместить централизованное администрирование и возможность легко настроить этот сервис на одном персональном компьютере?

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

Известные проблемы

Хотя Телеграмм и позволяет работать нескольким ботам с одним чат ИД, но когда идут два одновременных запроса GetUpdate, приходит ошибка - 409 conflict. В сервисе реализован механизм перезапросов, который на быстрых каналах хорошо справляется с этой ошибкой, но если интернет плохой, то проблема нарастает лавинообразно.

Статистику можно посмотреть отправив команду all/<имя компьютера> ver, в ответ придет версия сервиса, его аптайм и количество удачных, неудачных и пяти неудачных попыток подряд.

При тестировании в дата-центре с 12 серверами на одном боте ошибка появлялась чуть чаще чем никогда (0.2%), в тоже время пять компьютеров на кипрском ADSL могли получить до 50% ошибок.

Есть некоторые идеи, как улучшить эту ситуацию, но это уже в следующей версии.

Бонус

И в дополнении Powershell скрипт для массовой установки на доменные компьютеры (использует WinRM, соответственно надо запускать с того сервера, для которого WinRM открыт)

Скрипт
# Vars
$Servers = @("srv-1","srv-2","srv-3","srv-4")
$dataStamp = Get-Date -Format "yyyy-MM-dd"
 
$MSIPath = "\\srv-fs\distr\SLMF\currentVersion"
$LogPath = "\\srv-fs\distr\SLMF\InstallLog"
$ErrorPath = "\\srv-fs\distr\SLMF\errors"
 
$Servers | ForEach-Object {
 
  # multi-hop workaround
  if (Test-Path "$MSIPath") { 
    Copy-Item "$MSIPath\Service.Config.xml" \\$_\c$    Copy-Item "$MSIPath\ServiceLogonMultiFactor.msi" \\$_\c$  }
  
  Invoke-Command $_ {
    $MSIArgs = @(
        "/I c:\ServiceLogonMultiFactor.msi"
        "/L c:\SLMF_$($env:computername)_$($using:dataStamp).log"
    )
    # Uninstall to prevent doubles and for downgrade
    (Get-WmiObject -Query "SELECT * FROM Win32_Product WHERE Name like '%servicelogonmultifactor%'").uninstall()
 
    Start-Process "msiexec.exe" -ArgumentList $MSIArgs -Wait -NoNewWindow
    Start-Sleep -Seconds 2
    
    Get-Service ServiceLogonMultiFactor
 
  }
  # Check post-installation error log. Copy to central log repo
  if (($logs = Get-ChildItem "\\$_\c$\Program Files\Servilon\SLMF\log\errors") -ne $null) { 
    foreach ($file in $logs) { 
      Copy-Item -Path $file.FullName -Destination "$ErrorPath\$($_)_$($file.Name).txt" 
    }
  }
  # Move installation log to central log repo
  Move-Item "\\$_\c$\SLMF_*" "$LogPath\" -Force
 
  Remove-Item "\\$_\c$\ServiceLogonMultiFactor.msi"
  Remove-Item "\\$_\c$\Service.Config.xml"
}

Буду благодарен за разумную критику и оценку.