Перейти к содержимому

Серверный фингерпринтинг: 4 режима приватности

Серверный фингерпринтинг: 4 режима приватности

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

Код: Fingerprint-PHP, лицензия MIT.

composer require globus-studio/fingerprint, PHP 8.3 и новее, четыре режима приватности и работа с доверенными прокси, которая отказывается угадывать.

Зачем это нужно

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

Ни одному из этих случаев не нужна уверенность. Каждому нужен сигнал, достаточный для лишнего шага, и это фингерпринт дать может.

Четыре режима

strict собирает минимум сигналов и не хранит полный IP и сырые значения. balanced это рекомендованное умолчание для безопасности. maximum поднимает число сигналов и требует более сильного правового основания. custom позволяет приложению назвать свою политику.

Начинать с balanced и переходить к maximum только с записанной причиной это последовательность, которую принимает проверка приватности. Начинать с maximum и обещать снизить позже это последовательность, которую никто не заканчивает.

Прокси это место, где обычно ломается

За CDN или балансировщиком все запросы выглядят пришедшими с одного адреса. Лечится списком доверенных прокси, а не слепым чтением заголовков: withTrustedProxies(['10.0.0.0/8']) и withTrustedHeaders(['x-forwarded-for']), причём заголовки игнорируются, пока REMOTE_ADDR не в списке.

Это правило закрывает простую атаку, где клиент сам ставит X-Forwarded-For и выбирает себе отпечаток. Оно же первое, что проверяют после переезда: неверный список прокси даёт один идентификатор на всех посетителей и алерт, похожий на DDoS.

Совпадение это уровень, а не да или нет

compare() возвращает уровень совпадения, а level()->isSuspicious() это вопрос, который задаёт форма входа. Два отпечатка никогда не совпадут полностью, потому что версия браузера и размер экрана меняются.

Логическое «то же устройство» заставляет выбрать порог, который невозможно защитить на ревью. Уровень позволяет приложению сказать, что оно делает со средним совпадением, и обычно это просьба о втором факторе.

Чего это не может

Фингерпринтинг вероятностный. NAT, VPN, корпоративные прокси, мобильные сети, настройки приватности и обновления браузеров меняют сигналы, а человек, который переключается между Wi-Fi и мобильным интернетом, выглядит как два устройства.

README прямо пишет, что отпечаток не может быть единственным фактором аутентификации или блокировки. Команда, которая прочитала эту строку, строит второй фактор; команда, которая пропустила, запирает клиентов в отпуске.

Что проверить в первый день

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

Ни то ни другое не повод отказываться от подхода; оба повода сначала поработать в режиме отчёта.

Отдельно стоит проверить, что происходит при смене версии алгоритма. Если набор сигналов меняется, сохранённые отпечатки перестают совпадать, и об этом лучше знать из документа, а не из логов.

Счёт за приватность

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

Там, где решение записано, библиотека остаётся инструментом. Там, где нет, та же библиотека становится обязательством с журналом изменений.

Рядом: разработка на PHP о том, как считается такая интеграция, и разработка плагинов WordPress о контексте, в котором серверные проверки обычно живут.