Типичные ошибки реализации SMS-аутентификации

In English: https://blog.deteact.com/common-flaws-of-sms-auth/

Многие онлайн-сервисы используют SMS в качестве механизма аутентификации пользователей. Но допускаются маленькие ошибки, которые введут к крупным проблемам. Именно об этом пойдет речь в данной статье.


Введение

Данный метод аутентификации популярен в b2c-сервисах, поскольку повышает конверсию, ведь пользователю не нужно запоминать пароли, придумывать логины, а достаточно просто ввести свой номер телефона, на который придет SMS-код для подтверждения.

При этом исключаются некоторые типичные угрозы безопасности аккаунтов, как простые пароли, утечки баз паролей, простой фишинг.


Угрозы

Исходя из опыта участия в проектах по тестированию на проникновение, а также в программах bug bounty, мы выделили ряд основных угроз, которые могут быть реализованы в отношении механизма SMS-аутентификации.


Account Takeover (захват аккаунта)

Эта наиболее критичная угроза заключается в угоне аккаунта произвольного пользователя, например, при знании его номера телефона.

Регистрация от имени другого пользователя

Угроза имперсонации не так критична, но ущерб зависит от специфики сервиса. Как правило, если возможна имперсонация, возможен и угон уже зарегистрированного аккаунта, поскольку механизм подтверждения тот же.

SMS-флуд любых телефонных номеров

SMS-флуд может быть направлен как на пользователей системы, так и на любых абонентов, при этом для отправки сообщений используется собственно механизм аутентификации уязвимого веб-сервиса. Для самого веб-сервиса флуд чреват потерей лояльности клиентов, репутационным ущербом.

Истощение ресурсов веб-сервера и баланса на SMS-шлюзе

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


Уязвимости и атаки

Примеры уязвимостей всех описанных классов обнаруживались нами в рамках проектов по анализу защищённости, а также в рамках программ bug bounty (см. профиль https://hackerone.com/r0hack).

Отсутствие Rate Limit

Это наиболее общий класс уязвимостей SMS-аутентификации, и эксплуатация такого недостатка очевидна: код подтверждения можно просто перебрать, поскольку нет достаточных ограничений по количеству попыток.

Как правило, код подтверждения состоит из 4-6 цифр, так что максимальное количество запросов, необходимых для перебора, — 1 миллион, что совсем не много для современного веба.

Привязка Rate Limit к Cookie

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

Одинаковый код для разных действий

Уязвимость заключается в том, что код, высланный для подтверждения действия в одном интерфейсе (например, на странице регистрации) можно также использовать и для другого интерфейса (например, для входа в аккаунт).

Пример такой уязвимости из bug bounty: при входе в аккаунт приходил SMS-код, и перебирать его не получалось, потому что количество попыток для авторизации было ограничено, однако, если оправить этот же код в API для регистрации, то при отправке правильного кода приложение возвращало следующий ответ:

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

Отсутствие срока жизни кода

Бывает такое, что ограничения по количеству попыток отсутствуют, но у кода короткий срок жизни, и его сложно подобрать.

В рамках bug bounty была обнаружена уязвимость в таком механизме в одном из популярных сервисов: несмотря на то, что срок жизни 6-значного кода составлял всего 3 минуты (а отправить 1 миллион запросов за 3 минуты довольно сложно), в действительности срока жизни не было вообще, поскольку при повторной отправке кода приложение отправляло тот же самый код.

Видимо, программисты посчитали, что незачем снова генерировать случайный код, если предыдущий код никто не прочитал и не ввёл. Таким образом, для полного перебора 1 миллиона вариантов было достаточно запрашивать повторную отправку кода (и получать новые cookie) примерно каждые 2.5-3 минуты.

SMS-флуд

Rate limit должен ограничивать не только количество попыток входа с одним номером, но и в целом запросы к приложению, поскольку атакующий может попытаться осуществить флуд не на конкретного пользователя, а массово, с целью нанесения ущерба самому сервису.

Небезопасные случайные числа

Как известно, код подтверждения должен быть случайным, поэтому, если его можно предугадать (например, если его значение зависит только от текущей секунды по Unix-времени), любой аккаунт можно угнать.

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

Когда пользователь вводил код, на сервер отправлялся следующий объект:

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

Блокирование пользователей

Предыдущая уязвимость и соответствующая атака — частный случай DoS-атаки.

Если при превышении rate limit в приложении предусмотрена блокировка аккаунта пользователя, возможен массовый отказ в обслуживании: атакующий может просто осуществить несколько неуспешных попыток ввода кода для каждого из клиентов, таким образом заблокировав все аккаунты. Конечно, для этого нужно знать их номера телефонов или логины.

Перехват SMS

Не секрет, что SMS является небезопасным транспортом для передачи данных. Есть большое количество способов перехвата сообщений, включая атаки на уровне сигнальной сети SS7, атаки на абонента по GSM, компрометацию шлюза SMS-провайдера, компрометацию личного кабинета клиента на сайте оператора и т.д.

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

Рекомендации

Для снижения уровня риска призываем следовать следующим советам:

  • Использовать 6-значные коды подтверждения
  • Ограничивать количество и частоту попыток ввода кода подтверждения с одного IP-адреса
  • Учитывать количество попыток как в текущей сессии, так и суммарно для номера телефона
  • Не блокировать аккаунт пользователя после нескольких неудачных попыток
  • Для каждой попытки входа генерировать новый уникальный код
  • Для подтверждения каждого действия использовать отдельный код
  • Не использовать предсказуемые идентификаторы и коды подтверждения
  • Для особо критичных сервисов не использовать SMS-подтверждения, заменить на 2FA или хотя бы push-уведомления или звонками

Если вам нужно проанализировать защищённость вашего веб-приложения, пишите нам на [email protected].

2 Replies to “Типичные ошибки реализации SMS-аутентификации”

  1. А ещё — хранение логов отправленных SMS внутри Webroot, откуда можно прочитать отправленный код 🙂

    1. Ну для этого к вебруту нужно доступы еще получить. Да и не ошибка SMS-аутентификации, а не правильные настройки. Многие хранят отправляемые SMS прямо в txt файле на сервере и бывают доступны извне. Но это уже не ошибки SMS-аутентификации )

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *