Уязвимость тайны голосования при Электронном Голосовании: сценарий проверки.

1 июля 2020, 08:58
После публикации о проблемах с тайной ЭГ с нами через нашего бота связался аккаунт "ДИТ Москва" и попробовал убедить, что у администраторов нет технической возможности соотнести гражданина с его голосом. Господа, вы нас за кого держите?

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

Заранее поясним ключевую мысль: все ваши запросы можно логировать на сервере (ЭГ использует веб-сервер Nginx) ПОЛНОСТЬЮ, вместе с заголовками и потрохами, и складывать с разных серверов в единое хранилище, например, Elasticsearch, для последующей аналитики.

Поехали:

1. Заходим на сайт 2020og.ru и логинимся. В процессе вас перебросит на elec.2020og.ru, login.2020og.ru, потом обратно, и все ответы от этих серверов будут ставить вам cookie laravel_session=abcdef123 (содержимое у каждого из вас будет свое). Это те самые куки для идентификации пользователя, о которых сейчас каждый сайт спрашивает "хотите ли вы их принять". ДИТ не спрашивает, ДИТ просто их ставит.

2. Обратите внимание на запрос при проверке номера телефона на сайте ЭГ, вот он в сокращенном виде
POST https://elec.2020og.ru/common/ajax/confirm/sms/
Cookie: laravel_session=abcdef123
'type=sms&value=9261234567&voitingId=0'
Внимание, гипотеза (неопровержимая): сервер elec.2020og.ru сохранил этот запрос, содержащий ваш cookie и номер телефона, в логи и отправил в единое хранилище.

3. Соглашаемся с условиями и получаем бюллетень. Вот как происходит его выдача, тут происходит несколько переадресаций, видимо, это и есть тот самый "анонимайзер", но спойлер: он ничего не анонимизирует:
POST https://elec.2020og.ru/#complete
Cookie: laravel_session=abcdef123
ответ: HTTP/2 302 Found
location: https://elec.moscow/election/check/ длинная-длинная-цепочка-из-букв-и-цифр= 
(на самом деле тут скрыт ваш номер бюллетеня, допустим, 777-ggg-aaa. Как проверить: вставить эту длинную цепочку на сайт https://www.base64decode.org/, раскодировать, потом взять из результата url и еще раз раскодировать. Нас наперстками не проведешь!)

GET https://elec.moscow/election/check/ длинная-длинная-цепочка-из-букв-и-цифр=
ответ: HTTP/2 302 Found
location: https://elec.moscow/election/ 777-ggg-aaa (тот самый номер вашего бюллетеня, уже в открытом виде)
Видите? Сервер (никто не сможет этого опровергнуть) сохранил лог, в котором есть ваш cookie, уже связанный с номером телефона, и номер вашего бюллетеня!

4. Ставим галочку и отправляем голос. Ваш голос зашифровывается, создается транзакция и отправляется на сервер, чтобы потом попасть в блокчейн.
POST https://elec.moscow/election/vote
rawStoreBallotTx=транзакция_с_голосом
guid=777-ggg-aaa (номер вашего бюллетеня)
votingId=...
district=...
accountAddressBlock=...
keyVerificationHash=...
rawTxHash=xxxyyyzzz (вашу транзакцию можно будет найти в выгрузке блокчейна на сайте https://observer2020.mos.ru/observer/blocks-list по этому хэшу)

Что же, время собирать камни! После всего этого процесса в логах ДИТ, по неопровержимой гипотезе, хранится:
- связка cookie - номер телефона (laravel_session=abcdef123 и 9261234567)
- связка cookie - номер бюллетеня (laravel_session=abcdef123 и 777-ggg-aaa)
- связка номер бюллетеня - зашифрованный голос (777-ggg-aaa и транзакция_с_голосом, ищется в блокчейне по rawTxHash=xxxyyyzzz)

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