Как мы защищаем номера телефонов с помощью Oblivious Pseudorandom Function

По новому требованию закона онлайн-кинотеатры и другие аудиовизуальные сервисы должны передавать уполномоченной правительством исследовательской организации дополнительный идентификатор пользователя, сопоставленный с номером мобильного телефона. Если один человек указал один и тот же номер в разных сервисах, идентификатор тоже должен совпасть.
На первый взгляд, задача элементарная: нормализовать номер телефона и посчитать от него хеш с общим для всех компаний секретом. На практике простое решение создало бы единый секрет сразу для десятков независимых компаний — и единую точку компрометации. Поэтому мы хотели найти более надёжный способ защитить данные пользователей.
Меня зовут Айдар Сабиров, я занимаюсь информационной безопасностью в Яндексе. Порой нам приходится решать самые необычные задачи, в которых техническая часть оказывается лишь половиной решения. Вторая половина — договориться между компаниями и превратить непонятные формулы в работающий отраслевой процесс. Это как раз такой случай.
В этой статье я расскажу Хабру, как мы вместе с другими участниками рынка искали более надёжную конструкцию и пришли к схеме на основе Oblivious Pseudorandom Function (OPRF), предотвращающей отслеживание пользователя по его номеру.
Что именно требовалось сделать
Дано:
23 аудиовизуальных сервиса из специального реестра, передающие сведения, необходимые для измерения аудитории.
Уполномоченная исследовательская организация, которая с 2022 года измеряет интернет-аудиторию и собирает сведения, передаваемые аудиовизуальными сервисами.
Кстати, а зачем исследователям идентификаторы?
Один человек может смотреть онлайн-кинотеатр с телефона, ноутбука и телевизора. Совпадающий идентификатор помогает учитывать такого зрителя как одного человека, а не как несколько устройств. Он также позволяет сопоставлять аудиторию на разных площадках — например, понять, смотрели ли второй сезон сериала те же люди, что и первый сезон на другом сервисе. Для этого исследовательской организации нужны идентификаторы, но не сами номера телефонов.
Проблема: законодательство обязывает сервисы отправлять в уполномоченную исследовательскую организацию идентификатор, связанный с номером телефона, который пользователь указал при регистрации или позднее. Причём один и тот же номер должен давать один и тот же идентификатор у разных сервисов.
После осознания новых правил игры в голове появляются следующие мысли:
Каждый онлайн-кинотеатр знает номера телефонов своих пользователей. При этом обмен данными не должен раскрывать ему номера пользователей других сервисов, а исследовательской организации — номера любых пользователей.
«Одна ошибка, и ты ошибся». Любой серьёзный недочёт в схеме или её реализации позволит раскрыть номера телефонов не только Кинопоиска, но и всех остальных сервисов.
Нужно придумать схему обмена для онлайн-кинотеатров, которые напрямую конкурируют друг с другом за зрителей и не готовы раскрывать конкурентам данные о своей аудитории.
Получается, что нам нужен стабильный межсервисный псевдоним, который нельзя «раскрутить» обратно и восстановить исходный телефон при компрометации или недобросовестных действиях кого-то из участников.
Наивная схема: общий ключ
Самое очевидное решение выглядит так:
phone = Нормализация(номер_телефона)
id = hash(общий_ключ, phone)Все компании договариваются о формате номера и получают один общий секретный ключ. Под hash() подразумеваются Argon2, HMAC-SHA256 и прочие функции. При такой схеме одинаковый вход даёт одинаковый результат.

Работает ли наивная схема? Формально — да, функциональное требование выполняется. Защищает ли такая схема пользователя? Абсолютно нет. Достаточно утечки ключа у одного участника, и становится возможным подобрать все переданные телефоны от всех остальных компаний. Ключ может попасть в логи, исходный код, резервную копию или руки злоумышленника после взлома. Каковы шансы подобного события для такого большого числа компаний? Мы расцениваем как неприемлемо высокие.
Коротко о хешах
Хеширование — необратимая операция, этот фарш не провернуть назад. Допустим, у меня есть строка «qvk60\y=3Q[.Mz», SHA-256-хеш от которой будет равен «ae1862baef10aea59b68e6cbf6ab3d97f2a21fc295f7149f9831b38e3e6c053b». Имея на руках такой хеш, злоумышленник мало что может сделать. Однако никто не мешает пытаться подбирать исходные значения для заданного хеша в случае с телефонами.
Давайте вернёмся к телефонам и скажем, что у злоумышленника есть хеш «dbae582ee1f37b15623829ece40b66a8eb5162337ab0a8b7cf1b86cb300391bc». Тогда перебор будет происходить следующим образом:
79000000000 - 6b65f5502f34d9cf3d9bfcb3a014da96ea23be29fb5ba45d7f6bedfb654184cd
..
79123456788 - d2947981b20d06e5675ad1e16eb4c5399bcf2ccb9e41a4d84c8f1ac3b098a3db
79123456789 - dbae582ee1f37b15623829ece40b66a8eb5162337ab0a8b7cf1b86cb300391bc
(успех!)Как мы видим, если на вход подаётся значение с низкой энтропией, например, номер телефона, то подбор прообраза хеша остаётся делом техники и происходит порой за считанные секунды. Несмотря на то что в нашем случае речь идёт о телефонах с секретом, знание этого секрета злоумышленником сводит задачу к описанному выше перебору.
У наивной схемы было одно серьёзное преимущество: её очень легко реализовать. Поэтому часть участников предлагала остановиться именно на ней. Нам же хотелось сохранить детерминированность результата, но убрать общий секрет из инфраструктуры сервисов.
OPRF: функция, ключ которой клиент не узнаёт
Что ж, идея раздать всем одинаковый ключ не подходит, но как тогда поступить? Существует ли магия криптографии для нашей задачи? Да, подходящий примитив называется OPRF — Oblivious Pseudorandom Function. Это протокол между клиентом и сервером:
клиент знает
(допустим, это телефоны);
сервер знает секретный ключ
;
клиент получает результат преобразования
на секретном ключе сервера —
, но не узнаёт
;
сервер не узнаёт значения клиента
.
Получается, что сервер не раскрывает секретный ключ, а клиент не раскрывает чувствительные данные. Более подробно протокол описан в RFC 9497.
Ниже постараюсь объяснить базовую идею:
Номер телефона приводится к некоторому каноническому виду, например, из номера вырезаются плюс, скобки, пробелы (в нашем случае — это формат E.164).
Далее этот телефон превращается в точку на эллиптической кривой с помощью hash-to-curve.
Затем для каждого блока данных выбирается новый случайный ненулевой скаляр
и происходит «ослепление» (blinding):
Получившееся значение
называют ослеплённым. Компания отправляет его OPRF-серверу, который умножает точку на свой секретный скаляр
:
Компания получает
и снимает ослепление, используя обратное значение скаляра
известное только ей:
Здесь отлично подходит аналогия со школьной математикой и обычной операцией умножения. Множители и
сокращаются, а эллиптические кривые гарантируют, что без знания скаляра
сервер не восстановит исходное значение.
После финального хеширования и сериализации N превращается в идентификатор фиксированной длины. Одинаковый канонический номер телефона при одном и том же серверном ключе даёт одинаковый результат. При этом компания не узнаёт ключ, а сервер вместо телефона видит случайно ослеплённую точку.
Схема с OPRF и Третьей стороной
Заручившись этими знаниями, мы придумали первую версию схемы, которая выглядит так:

По сравнению с общим ключом это уже большой шаг вперед:
номера телефонов не передаются Третьей стороне или уполномоченной исследовательской организации;
одинаковые номера дают одинаковые идентификаторы;
аудиовизуальные сервисы не хранят общий ключ и не могут случайно раскрыть его.
Возникает резонный вопрос: кто такая эта ваша «Третья сторона»? Возникает немалое давление на эту «сущность», а ещё есть возможность сговора и банальных ошибок. В текущем виде схема тоже не подходит, следует придумать более надёжный вариант.
Финальная схема с OPRF и двумя Третьими сторонами
Для придумывания более надёжного варианта следует задаться вопросом: какие именно проблемы мы видим в схеме с OPRF и возможно ли их минимизировать?
Нужно было ответить на три вопроса:
Как выбрать Третью сторону? Что, если она окажется ненадёжной или будет действовать злоумышленно?
Как снизить риск того, что Третья сторона вступит в сговор с уполномоченной исследовательской организацией и передаст ей свой секретный ключ?
Как разделить ответственность между несколькими компаниями?
Мы решили распределить обработку между независимыми друг от друга компаниями, каждая со своим секретным ключом. Роль одной из Третьих сторон взяли на себя мы, второй стала ещё одна крупная интернет-компания. Передачи одного ключа исследовательской организации недостаточно для самостоятельного вычисления итоговых идентификаторов.
В текущей реализации мы остановились на двух Третьих сторонах. Это позволяет распределить доверие между независимыми участниками. При этом онлайн-кинотеатрам нужно подключиться только к двум сервисам обработки: каждая новая Третья сторона усложняла бы реализацию и добавляла зависимость от её доступности.
Финальная схема с двумя сторонами и ключами ,
выглядит так:

Порядок взаимодействия с Третьими сторонами не важен: скалярные умножения коммутируют, поэтому . Практически это означает следующее:
1. Компания нормализует номер, отображает его в точку на эллиптической кривой и ослепляет новым случайным :
2. Третья сторона 1 применяет свой ключ и возвращает результат:
3. Компания передаёт этот результат Третьей стороне 2.
4. Третья сторона 2 применяет второй ключ :
5. Компания снимает собственное ослепление и финализирует результат:
6. В уполномоченную исследовательскую организацию уходит только стабильный итоговый идентификатор id. Перед отправкой id хешируется для минимизации поверхности атаки. Онлайн-кинотеатры отправляют итоговые идентификаторы только в уполномоченную исследовательскую организацию. Данные о просмотрах получает также только она — Третьим сторонам онлайн-кинотеатры их не передают.
В итоге ни у одного аудиовизуального сервиса нет общего отраслевого ключа, а компрометации одной стороны недостаточно, чтобы самостоятельно вычислять итоговые идентификаторы для произвольных номеров. Для офлайн-перебора потребуются оба ключа и доступ к финальным значениям.
Свойства схемы
Давайте традиционно пройдёмся по преимуществам и недостаткам.
Что становится лучше
Утечка в одном аудиовизуальном сервисе не раскрывает отраслевой ключ. Такого ключа у сервисов больше нет.
Третьи стороны не видят телефоны. Они получают только ослеплённые точки, причём новый скаляр
используется для каждого нового набора данных.
Компрометации одной Третьей стороны недостаточно для полного офлайн-перебора. Атакующему необходимо получить ещё один ключ.
Доверие можно распределять дальше. Математически участников может быть больше двух, хотя за это придётся платить сложностью, доступностью и скоростью работы всей схемы.
Что остаётся риском
Злонамеренный клиент может использовать протокол как оракул. Участник с легальным доступом способен попытаться прогнать через обе стороны не только номера своих пользователей, но и весь предполагаемый диапазон, а затем сохранить таблицу «телефон → идентификатор». Мы постарались минимизировать риск подобной атаки на организационном уровне, установив ограничение сверху на количество телефонов (Третьи стороны видят количество записей и обнаружат злоупотребление), а также предусмотрев возможность смены секретных ключей.
Взломанный сервис раскрывает известные ему соответствия. Компания по необходимости знает номера собственных пользователей и получает их итоговые идентификаторы. Если злоумышленник украдёт готовую таблицу «телефон → идентификатор», он сможет связать эти идентификаторы с событиями того же номера в других сервисах. В отличие от утечки общего ключа, атака по умолчанию ограничена номерами, которые уже были известны пострадавшей компании.
Сговор обеих Третьих сторон с уполномоченной исследовательской организацией ломает основное допущение. Если оба ключа и итоговые значения окажутся у одного атакующего, он сможет снова строить словарь по пространству телефонных номеров. Мы считаем подобное событие крайне маловероятным, так как каждая из сторон является конкурентом друг другу и не имеет общих интересов с этой организацией.
Метаданные остаются видимыми. Третьи стороны видят, какой аудиовизуальный сервис отправляет запросы, когда и сколько записей передаёт на обработку. Сами номера телефонов скрыты ослеплением, но объём и периодичность запросов могут раскрывать сведения о работе сервиса. Часть сервисов не раскрывает размер своей аудитории публично и считает эту информацию чувствительной. Поэтому мы предусмотрели возможность скрыть реальное количество номеров телефонов, передавая Третьим сторонам на обработку немного больше записей в рамках лимита. Можно передавать и одинаковые номера: для каждого блока данных используется новый скаляр .
Появляется зависимость от доступности двух сервисов. Если хотя бы одна сторона недоступна, новые идентификаторы вычислить нельзя. Решаем это с помощью вычислительных мощностей и высоких требований доступности.
Итоги
Наивное решение задачи умещалось в одну строку с хешированием, но требовало раздать один секрет множеству независимых компаний. Мы заменили эту единую точку компрометации протоколом, в котором:
сервисы получают одинаковый идентификатор для одинакового номера;
исходные номера не уходят уполномоченной исследовательской организации или Третьим сторонам;
общий ключ не появляется у аудиовизуальных сервисов;
доверие разделено между двумя организациями;
массовый перебор дополнительно сдерживается лимитами и контролем запросов.
В криптографии существует невероятное количество примитивов и способов элегантно решить неожиданно большое количество задач. Придумать схему или протокол — это полбеды, настоящие проблемы начинаются при попытке воплотить навороченные схемы в реальную жизнь, где приходится сталкиваться с недоверием, нежеланием что-либо менять или равнодушием. Описанная в статье финальная схема — это результат компромисса. Кому-то она покажется перегруженной, кому-то — недостаточной, а кому-то — как раз. Что думаете вы?
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.