Исследование причин аномального голосования на сайте РОИ или особенности электронной демократии в России
За сайтом «Российские общественные инициативы» я наблюдаю давно, примерно с 29 мая 2013 года. Как и другие наблюдатели, я замечал аномалии в ходе голосований за различные инициативы. Но это мало кого беспокоило, пока аномалии приводили по нашим оценкам к росту числа голосов. Видимо, никто не считал чем-то плохим, если очередная инициатива наберет 100 000 голосов раньше срока. Всё изменилось, когда аномалии стали замедлять голосование.
Это началось 24 ноября в 13:35 по московскому времени. Счетчик голосов за принятие инициативы 9376 уменьшился на 2. Потом еще на 1 и еще на 2. Вечером уменьшение значения счетчика стало происходить всё чаще и чаще. Кто-то заметил это и сообщил автору инициативы. С этого момента начался тщательный мониторинг хода голосования.
Я расскажу про некоторые странности голосования, которые мы (наблюдатели) заметили за последнюю неделю. Также я попытаюсь сделать предположения о причинах некоторых из них. Выводов довольно мало, т.к. не всегда есть возможность получить нужные данные о ходе голосования.
По графикам быстро стало понятно, что в определенные моменты изменение счетчика происходит более резко. Более того, эти моменты одинаковы для каждого часа с точностью до нескольких секунд. Например, на наших логах пики прироста голосов наблюдались на минутах 3, 9, 15, 21, 27, 33, 39, 45, 51, 57. Через несколько дней минуты сдвинулись на одну (стало 4, 10, 16...), но на промежутке в несколько часов они всё так же попадали в заданную минуту и секунду.
Итак, вероятность получить голос зависит от конкретной минуты. Для некоторых инициатив эта зависимость выражена ярко:
Для некоторых — слабее:
Где-то почти незаметна:
Но самое интересное происходит с инициативой «Об уголовной ответственности за незаконное обогащение чиновников». В ней наблюдается зависимость только для уменьшения количества голосов:
Главное, что мне захотелось выяснить: являются ли шестиминутные пики на счетчике результатом работы внешнего по отношению к РОИ скрипта, или это внутренний механизм сайта (кеширование, ошибка, злой умысел).
Возможность голосования ботами вполне реальна. Для входа на РОИ используется аккаунт на сайте «Госуслуги». Реквизиты доступа к Госуслугам можно воровать точно так же, как и аккаунты ВКонтакте. Даже легче, поскольку Госуслуги не принимают специальных мер по защите от несанкционированного доступа. Например, они не блокируют аккаунты при входе на сайт из новых мест.
Голосование на сайте происходит отправкой post-запроса на адрес www.roi.ru/ajax/voting. Голосующий пользователь определяется по cookies (SESSIONID). Инициатива и тип голосования задаются параметрами POST-запроса. Очевидно, что количество запросов к серверу должно быть равно количеству голосов. Мы предположили, что при уменьшении интервала сбора данных должны быть видны голоса отдельных пользователей/ботов. С голосами вне регулярных пиков достаточно записывать данные раз в две секунды, чтобы разделить большинство голосов:
Пакеты из нескольких отрицательных голосов не удается разделить по времени. Группа голосов всегда добавляется/вычитается одновременно (в пределах погрешности изменения порядка 300 мс, связанной с временем отклика сервера и обработки запроса). Максимальная группа, которую удалось наблюдать, состояла из 7 голосов (уменьшение счетчика). Характерное время работы POST-запроса голосования (/ajax/voting/) — 300..350 мс. Маловероятно, что 7 таких достаточно тяжелых запросов можно выполнить с удаленного сервера за секунду и не допустить ни одного сбоя за всю историю наблюдений.
Как такое может быть?
— Запросы могут выполняться с соседнего компьютера и сознательно запускаются синхронно в несколько потоков (что не имеет смысла, если хочется показать естественный ход голосования).
— Или же появление групп голосов связано с внутренними механизмами сайта РОИ.
Представители РОИ не отрицают, что у них есть какой-то код, который приводит к появлению пакетов, но внятного объяснения не дают. Какие внутренние механизмы могут давать картину с пиками на графике прироста голосов?
— кеширование счетчика
— replication lag при репликации базы данных по схеме Master-Slave
— что-то другое
Для проверки влияния этих факторов мы стали запускать голосование и отзыв голоса скриптом, отслеживая изменение показаний счетчиков. Тут сразу возникла проблема. На странице инициативы есть три счетчика: верхний счетчик общего количества голосов, нижний счетчик общего количества голосов и часовой счетчик в правой колонке (он нас пока не интересует).
При голосовании через сайт мы видели, что счетчики общего количества голосов меняются сразу же (с точностью до времени перезагрузки страницы) и синхронно. При голосовании скриптом менялся только верхний счетчик. Через несколько минут показания верхнего счетчика возвращались к прежнему значению. Более того, минуты и секунды отката счетчика совпадали с минутами и секундами скачкообразных изменений счетчика! Значит, на сайте есть механизм, корректирующий показания счетчика (счетчиков) по определенному расписанию. Вероятно, этот же механизм ответственен за скачкообразные изменения счетчиков, т.к. время изменений совпадает с графиком его работы.
Оказалось, что для корректной работы API голосования нужно передать не только id инициативы (petition_id), но и некий decision_id, уникальный для каждой инициативы. Если передать оба параметра, то голос бота учитывается немедленно в обоих счетчиках и не пропадает через несколько минут. Назначение decision_id нам неизвестно.
Теперь можно попробовать разобраться с природой внутреннего механизма, меняющего показания счетчиков. Напомню, что мы предполагали возможность влияния кеширования или задержки при репликации базы. Мы стали запускать голосование «за» и отмену голоса в цикле каждые 30 секунд. Между голосованием и отменой было от 5 до 20 секунд в разных опытах. После каждого голосования/отзыва запускалась проверка показаний счетчиков. Они всегда менялись синхронно. Были сделаны сотни измерений в разное время и на разных инициативах. Ни разу не было случая, когда счетчики не прореагировали бы на голос. Пример выдачи скрипта:
refresh
200 72019 72019 2014-12-01 13:26:12
голосование
200 success_affirmative 2014-12-01 13:26:16
refresh
200 72020 72020 2014-12-01 13:26:21
отмена
200 success_reject 2014-12-01 13:27:05
refresh
200 72019 72019 2014-12-01 13:27:10
При этом пакетные голоса как поступали, так и продолжали поступать:
Как может кеширование влиять на одни голоса, но никогда не влиять на проверочные?
Мы также обратили внимание, что скачкообразное изменение количества голосов происходит не синхронно для двух счетчиков. Нижний счетчик «догоняет» верхний примерно через минуту. Такое поведение не характерно для обычного голосования через сайт. Это больше похоже на голосование ботом без параметра decision_id с той разницей, что при голосовании без decision_id инкремент верхнего счетчика отменяется, а в данном случае мы видим, что нижний счетчик подстраивается под верхний:
дата/время | нижний счетчик | верхний счетчик | голоса в час
2014-12-01 00:48:06 87947 87947 1
2014-12-01 00:48:12 87948 87948 1 +1 (голос)
2014-12-01 00:48:16 87948 87948 1
…
2014-12-01 00:51:51 87948 87948 1
2014-12-01 00:51:57 87948 87947 1 -1 (асинхронная отмена голоса)
2014-12-01 00:52:02 87948 87947 1
2014-12-01 00:52:06 87948 87947 1
2014-12-01 00:52:11 87948 87947 1
2014-12-01 00:52:17 87948 87947 1
2014-12-01 00:52:21 87948 87947 1
2014-12-01 00:52:27 87948 87947 1
2014-12-01 00:52:32 87948 87947 1
2014-12-01 00:52:36 87948 87947 1
2014-12-01 00:52:42 87947 87947 1 коррекция счетчика
2014-12-01 00:52:46 87947 87947 1
…
2014-12-01 01:14:06 87947 87947 1
2014-12-01 01:14:12 87948 87948 1 +1 (голос)
2014-12-01 01:14:16 87948 87948 1
…
2014-12-01 01:16:46 87948 87948 2
2014-12-01 01:16:52 87949 87949 2 +1 (голос)
2014-12-01 01:16:56 87949 87949 2
…
2014-12-01 01:21:52 87949 87949 2
2014-12-01 01:21:57 87949 87948 2 -1 (асинхронная отмена голоса)
2014-12-01 01:22:01 87949 87948 2
2014-12-01 01:22:07 87949 87948 2
2014-12-01 01:22:11 87949 87948 2
2014-12-01 01:22:17 87949 87948 2
2014-12-01 01:22:22 87949 87948 3
2014-12-01 01:22:26 87949 87948 3
2014-12-01 01:22:32 87949 87948 3
2014-12-01 01:22:36 87949 87948 3
2014-12-01 01:22:42 87948 87948 3 коррекция счетчика
2014-12-01 01:22:46 87948 87948 3
…
2014-12-01 01:26:06 87948 87948 3
2014-12-01 01:26:12 87949 87949 3 +1 (голос)
2014-12-01 01:26:16 87949 87949 3
2014-12-01 01:26:22 87949 87949 3
2014-12-01 01:26:26 87949 87949 3 -1 (отзыв голоса)
2014-12-01 01:26:32 87948 87948 3
2014-12-01 01:26:36 87948 87948 3
2014-12-01 01:26:42 87949 87949 3 +1 (голос)
2014-12-01 01:26:46 87948 87948 3 -1 (отзыв голоса)
2014-12-01 01:26:52 87948 87948 3
2014-12-01 01:26:56 87948 87948 3
2014-12-01 01:27:02 87948 87948 3
В какой-то момент выяснилось, что у домена www.roi.ru два IP смотрят в мир.
www.roi.ru. 299 IN A 109.207.2.37Было сделано предположение, что это два фронтенд-сервера, между которыми может быть настроена репликация, дающая задержку. Был написан скрипт, синхронно собирающий данные счетчиков с этих двух IP-адресов. Результат совпадал в пределах двух секунд. То есть, рассинхронизация фронтендов не могла дать описанные выше эффекты.
www.roi.ru. 299 IN A 109.207.2.34
Результат
На данный момент причины появления регулярных пакетных прибавлений и вычитаний голосов нам не понятны. Больше похоже, что это внутренний фактор, но его природа остается загадкой. В комментариях прошу высказывать свои версии происходящего (технические, а не конспирологические).
Совсем недавно появился хороший детальный анализ графиков голосования. Лично я согласен не со всеми выводами автора. Например, многие выводы сделаны с предположением, что робот может голосовать идеально одновременно с нескольких аккаунтов. В реальности такое возможно только если робот находится на том же сервере, что и база данных РОИ.
Информация по теме
- Пост с другими примерами аномалий голосования
- Ход голосования на графике, график с суммой голосов за каждые сутки
- Поминутный график, на котором видны групповые отзывы голосов
- Еще один график с приростом голосов по минутам
- Прекрасный график с высокой детализацией по времени
Update
РОИ выкатили обновление:
— отозвать голос теперь нельзя;
— счетчики обновляются не сразу же после голосования, а раз в минуту.
Теперь вы даже не можете убедиться, что ваш голос учли;
— шестиминутные аномалии выключили (собственно, это доказывает, что их делал не посторонний робот, а РОИ).
Если отключение отзыва — это, в целом, положительный момент, то кеширование счетчиков — это сильный удар по независимому мониторингу. Тем не менее, у голосований есть еще множество странных моментов. Пожалуй, пора готовить продолжение поста.
Update 2
Руководитель Фонда электронной демократии Илья Массух рассказал о новостях проекта. Но как-то странно… Пишет, что раньше было кеширование, хотя эксперементально нам так и не удалось его найти. Пишет, что вывод инициативы в топ автоголосованием стал для него открытием, хотя это не так (сам автор накруточной инициативы заспамил всё РОИ этой информацией). Происходящее объясняет тем, что «вся эта активность пришлась параллельно на процесс совершенствования алгоритмов» (перевожу: свет с Венеры отразился от верхних слоёв атмосферы и вызвал взрыв болотного газа). Но теперь алгоритм действительно улучшился: появился кеш, поэтому в пределах минуты с вашим голосом можно делать что угодно, никто об этом даже не узнает.